24

我们遇到了一个奇怪的问题ConcurrentHashMap,两个线程似乎在调用put(),然后在方法中永远等待Unsafe.park()。从外面看,里面好像是个僵局ConcurrentHashMap

到目前为止,我们只见过这种情况发生一次。

谁能想到可能导致这些症状的任何事情?

编辑:相关线程的线程转储在这里:

“[编辑] 线程 2”prio=10 tid=0x000000005bbbc800 nid=0x921 等待条件 [0x0000000040e93000]
   java.lang.Thread.State:等待(停车)
    在 sun.misc.Unsafe.park(本机方法)
    - 停车等待 <0x00002aaaf1207b40> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
    在 java.util.concurrent.locks.LockSupport.park(LockSupport.java:158)
    在 java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:747)
    在 java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:778)
    在 java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1114)
    在 java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:186)
    在 java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:262)
    在 java.util.concurrent.ConcurrentHashMap$Segment.put(ConcurrentHashMap.java:417)
    在 java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:883)
    在[编辑]


“[编辑] 线程 0”prio=10 tid=0x000000005bf38000 nid=0x91f 等待条件 [0x000000004151d000]
   java.lang.Thread.State:等待(停车)
    在 sun.misc.Unsafe.park(本机方法)
    - 停车等待 <0x00002aaaf1207b40> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
    在 java.util.concurrent.locks.LockSupport.park(LockSupport.java:158)
    在 java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:747)
    在 java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:778)
    在 java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1114)
    在 java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:186)
    在 java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:262)
    在 java.util.concurrent.ConcurrentHashMap$Segment.put(ConcurrentHashMap.java:417)
    在 java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:883)
    在[编辑]
4

3 回答 3

11

我不认为这就是您的情况,但是可以使用单个实例编写死锁ConcurrentHashMap,并且只需要一个线程!让我卡了好一阵子。

假设您正在使用 aConcurrentHashMap<String, Integer>来计算直方图。你可能会做这样的事情:

int count = map.compute(key, (k, oldValue) -> {
    return oldValue == null ? 1 : oldValue + 1;
});

哪个工作得很好。

但是,假设您决定改为这样写:

int count = map.compute(key, (k, oldValue) -> {
    return map.putIfAbsent(k, 0) + 1;
});

您现在将获得一个带有如下堆栈的 1 线程死锁:

Thread [main] (Suspended)   
    owns: ConcurrentHashMap$ReservationNode<K,V>  (id=25)   
    ConcurrentHashMap<K,V>.putVal(K, V, boolean) line: not available    
    ConcurrentHashMap<K,V>.putIfAbsent(K, V) line: not available    
    ConcurrentHashMapDeadlock.lambda$0(ConcurrentHashMap, String, Integer) line: 32 
    1613255205.apply(Object, Object) line: not available    
    ConcurrentHashMap<K,V>.compute(K, BiFunction<? super K,? super V,? extends V>) line: not available  

在上面的示例中,很容易看出我们试图在原子修改内部修改映射,这似乎是个坏主意。map.compute但是,如果在调用和之间有一百个事件回调堆栈帧map.putIfAbsent,则很难追踪。

于 2015-12-02T09:05:21.880 回答
4

也许不是您想要的答案,但这可能是一个 JVM 错误。请参阅JDK 6865591

Test6471091.java 在 Solaris-i586 上挂起

于 2010-07-20T18:43:44.117 回答
3

包 Unsafe 是原生的,实现依赖于平台。

在地图上获得锁的第三个线程(在平台级别,异常不是问题)的突然​​终止可能导致这种情况 - 锁状态被破坏,另外两个线程被禁用并等待有人调用 Unsafe.unpark() (这永远不会发生)。

于 2010-07-21T00:58:35.377 回答