ConcurrentHashMap.get()是否保证通过不同的线程看到以前的ConcurrentHashMap.put()?

Stu*_*son 22 java multithreading concurrenthashmap

是否保证通过不同的线程看到以前的?我的期望是,并且阅读JavaDocs似乎表明了这一点,但我99%确信现实是不同的.在我的生产服务器上,下面似乎正在发生.(我已经记录了它.)ConcurrentHashMap.get() ConcurrentHashMap.put()

伪代码示例:

static final ConcurrentHashMap map = new ConcurrentHashMap();
//sharedLock is key specific.  One map, many keys.  There is a 1:1 
//      relationship between key and Foo instance.
void doSomething(Semaphore sharedLock) {
    boolean haveLock = sharedLock.tryAcquire(3000, MILLISECONDS);

    if (haveLock) {
        log("Have lock: " + threadId);
        Foo foo = map.get("key");
        log("foo=" + foo);

        if (foo == null) {
            log("New foo time! " + threadId);
            foo = new Foo(); //foo is expensive to instance
            map.put("key", foo);

        } else
            log("Found foo:" + threadId);

        log("foo=" + foo);
        sharedLock.release();

    } else
        log("No lock acquired");
} 
Run Code Online (Sandbox Code Playgroud)

似乎正在发生的事情是这样的:

Thread 1                          Thread 2
 - request lock                    - request lock
 - have lock                       - blocked waiting for lock
 - get from map, nothing there
 - create new foo
 - place new foo in map
 - logs foo.toString()
 - release lock
 - exit method                     - have lock
                                   - get from map, NOTHING THERE!!! (Why not?)
                                   - create new foo
                                   - place new foo in map
                                   - logs foo.toString()
                                   - release lock
                                   - exit method
Run Code Online (Sandbox Code Playgroud)

所以,我的输出看起来像这样:

Have lock: 1    
foo=null
New foo time! 1
foo=foo@cafebabe420
Have lock: 2    
foo=null
New foo time! 2
foo=foo@boof00boo    
Run Code Online (Sandbox Code Playgroud)

第二个线程没有立即看到放!为什么?在我的生产系统上,有更多的线程,我只看到一个线程,第一个紧跟在线程1之后,有一个问题.

我甚至尝试将ConcurrentHashMap上的并发级别缩减到1,而不是它应该重要.例如:

static ConcurrentHashMap map = new ConcurrentHashMap(32, 1);
Run Code Online (Sandbox Code Playgroud)

我哪里错了?我的期望?或者我的代码(真正的软件,而不是上面的代码)中有一些错误导致了这个问题吗?我反复思考过,99%肯定我正确处理锁定.我甚至无法理解ConcurrentHashMapJVM中的错误. 请救我自己.

可能相关的Gorey细节:

  • 四核64位Xeon(DL380 G5)
  • RHEL4(Linux mysvr 2.6.9-78.0.5.ELsmp #1 SMP... x86_64 GNU/Linux)
  • Java 6(build 1.6.0_07-b06,64-Bit Server VM (build 10.0-b23, mixed mode))

Dav*_*sel 10

基于在高速缓存中找不到它而在高速缓存中创建昂贵的创建对象的问题是已知问题.幸运的是,这已经实施了.

您可以使用地图制作工具谷歌Collecitons.您只需给它一个回调来创建您的对象,如果客户端代码在地图中查找并且映射为空,则调用回调并将结果放入映射中.

MapMaker javadocs ......

 ConcurrentMap<Key, Graph> graphs = new MapMaker()
       .concurrencyLevel(32)
       .softKeys()
       .weakValues()
       .expiration(30, TimeUnit.MINUTES)
       .makeComputingMap(
           new Function<Key, Graph>() {
             public Graph apply(Key key) {
               return createExpensiveGraph(key);
             }
           });
Run Code Online (Sandbox Code Playgroud)

顺便说一句,在您的原始示例中,使用ConcurrentHashMap没有任何优势,因为您锁定了每个访问,为什么不在锁定的部分中使用普通的HashMap?


Cow*_*wan 7

这里有一些好的答案,但据我所知,没有人实际上提出了一个问题的规范答案:"ConcurrentHashMap.get()保证通过不同的线程看到以前的ConcurrentHashMap.put()".那些说"是"的人没有提供消息来源.

所以:是的,它是有保证的.来源(请参阅"内存一致性属性"一节):

在将对象放入任何并发集合之前的线程中的操作发生在从另一个线程中的集合访问或移除该元素之后的操作之前.

  • 我不认为这回答了这个问题.我将引用解释为如果你执行put,那么线程在put之前完成的所有其他内容也将在获取成功时执行获取的线程可见.(get可能仍然会找到null或old值,只要没有其他任何其他放置线程已完成的内容变得对get线程可见).IOW,没有严格的时间保证,但有相对订购保证.但OPs示例不应受排序影响.. (2认同)