ConcurrentHashMap有可能“死锁”吗?

问题描述 投票:0回答:4

我们遇到了一个关于

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

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

有人能想到什么可能导致这些症状吗?

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


“[已编辑]线程 2”prio=10 tid=0x000000005bbbc800 nid=0x921 等待条件 [0x0000000040e93000]
   java.lang.Thread.State:等待(停车)
    在 sun.misc.Unsafe.park(本机方法)
    - 停车等待<0x00002aaaf1207b40>(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>(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)
    在[已编辑]
java concurrency concurrenthashmap
4个回答
11
投票

我不认为这就是你的情况,但是用单个

ConcurrentHashMap
实例编写死锁是可能的,而且它只需要一个线程! 让我困了好一阵子。

假设您正在使用

ConcurrentHashMap<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
之间存在一百个事件回调堆栈帧,那么追踪起来可能会非常困难。


4
投票

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

Test6471091.java 在 Solaris-i586 上挂起


3
投票

Package Unsafe 是原生的,实现取决于平台。

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


0
投票

我能够使用至少两个线程在

.clear()
内部使用
.compute
导致死锁,请参阅MRE

解释

  • 线程 1 使用
    key 1
    方法
     获取 
    compute
  • 上的锁
  • 线程 2 使用
    key 2
    方法
     获取 
    compute
  • 上的锁
  • 线程1调用
    clear
    方法清除缓存,现在等待线程2退出
    compute
    方法
  • 线程 2 调用
    clear
    方法,现在等待线程 1 退出
    compute
    clear
    方法
  • 死锁:/

this.store.compute(key, (k, v) -> {

  log("status=computing, key=%s", key);

  sleep(1_000);

  if (r.nextBoolean()) {
    log("status=clearing, key=%s", key);
    this.store.clear();
  }

  if (isEmpty(v)) {
    log("status=computed, key=%s", key);
    return r.nextBoolean();
  }

  return v;
});

  Number of locked synchronizers = 1
  - java.util.concurrent.ThreadPoolExecutor$Worker@378bf509
© www.soinside.com 2019 - 2024. All rights reserved.