Возможно ли для ConcurrentHashMap"тупик"?
Мы столкнулись со странной проблемой с ConcurrentHashMap
, где два потока, кажется, вызывают put()
, а затем ждут вечно внутри метода Unsafe.park()
. Снаружи это выглядит как тупик внутри ConcurrentHashMap
.
EDIT : дамп потока для соответствующих потоков находится здесь:
"[redacted] Thread 2" prio=10 tid=0x000000005bbbc800 nid=0x921 waiting on condition [0x0000000040e93000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00002aaaf1207b40> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:158) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:747) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:778) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1114) at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:186) at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:262) at java.util.concurrent.ConcurrentHashMap$Segment.put(ConcurrentHashMap.java:417) at java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:883) at [redacted] "[redacted] Thread 0" prio=10 tid=0x000000005bf38000 nid=0x91f waiting on condition [0x000000004151d000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00002aaaf1207b40> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:158) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:747) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:778) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1114) at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:186) at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:262) at java.util.concurrent.ConcurrentHashMap$Segment.put(ConcurrentHashMap.java:417) at java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:883) at [redacted]
3 ответа:
Я не думаю, что это то, что происходит в вашем случае, но можно написать тупик с одним экземпляром
Предположим, вы используете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
имеется сотня стековых кадров событий-обратных вызовов, то их довольно трудно отследить.
Пакет Unsafe является нативным, реализация зависит от платформы.
Внезапное завершение третьего потока (на уровне платформы, exception не является проблемой), который получил блокировку на карте, может вызвать такую ситуацию - состояние блокировки нарушено, два других потока отключены и ждут, когда кто-то вызовет небезопасный.unpark () (и этого никогда не произойдет).