È possibile per concurententhashmap a "deadlock"?
-
18-09-2020 - |
Domanda
Abbiamo incontrato uno strano problema con ConcurrentHashMap
, in cui due thread sembra chiamare put()
, quindi in attesa per sempre all'interno del metodo Unsafe.park()
.Dall'esterno, sembra un deadlock all'interno ConcurrentHashMap
.
Abbiamo visto solo questo accadere una volta finora.
Qualcuno può pensare a tutto ciò che potrebbe causare questi sintomi?
Modifica : La discarica del thread per i thread pertinenti è qui:
"[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].
Soluzione
Forse non la risposta che vuoi, ma questo potrebbe essere un bug JVM.Vedi
http://bugs.sun.com/bugdatabase/view_bug.do? Bug_id= 6865591
Altri suggerimenti
Non penso che questo sia ciò che sta accadendo nel tuo caso, ma è possibile scrivere un deadlock con un'unica istanza di ConcurrentHashMap
e necessita solo di un thread!Mi ha tenuto bloccato per un bel po '.
Diciamo che stai usando un ConcurrentHashMap<String, Integer>
per calcolare un istogramma.Potresti fare qualcosa del genere:
int count = map.compute(key, (k, oldValue) -> {
return oldValue == null ? 1 : oldValue + 1;
});
.
che funziona bene.
Ma diciamo che decidi invece di scriverlo come questo:
int count = map.compute(key, (k, oldValue) -> {
return map.putIfAbsent(k, 0) + 1;
});
.
Otterrai ora un deadlock a 1 fili con uno stack come questo:
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
.
Nell'esempio sopra, è facile vedere che stiamo tentando di modificare la mappa all'interno di una modifica atomica, che sembra una cattiva idea.Tuttavia, se ci sono un centinaio di fotogrammi di stack di callback-callback tra la chiamata a map.compute
e map.putIfAbsent
, quindi può essere abbastanza difficile da rintracciare.
Pacchetto non sicuro è nativo, un'attuazione dipende da una piattaforma.
Terminazione brusca del terzo thread (a livello di piattaforma, l'eccezione non è un problema) che ha acquisito un blocco sulla mappa può causare tale situazione: lo stato di blocco è rotto, altri due fili sono disabilitati e in attesa di qualcuno di chiamare non sicuro.unpark() (E questo non accadrà mai).