2010-07-20 6 views
21

Abbiamo riscontrato uno strano problema con ConcurrentHashMap, in cui due thread sembrano chiamare put() e quindi attendere per sempre all'interno del metodo Unsafe.park(). Dall'esterno, sembra un deadlock all'interno di ConcurrentHashMap.È possibile che ConcurrentHashMap "deadlock"?

Abbiamo visto questo solo una volta finora.

Qualcuno può pensare a qualcosa che potrebbe causare questi sintomi?

EDIT: Il dump filo per i fili rilevanti è 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] 
+6

Avete il dump del thread? –

+0

@ John W .: buon punto. Lo posterò appena riesco a toglierlo dal server. –

+0

C'è qualche altra parte del dump del thread che mostra quale thread possiede effettivamente il lock? Queste discussioni stanno semplicemente aspettando di essere acquisite. Trovare ciò che stanno aspettando può aiutare. –

risposta

4

Forse non è la risposta che si desidera, ma questo può essere un bug JVM. Vedere

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6865591

+0

Anche questo bug correlato sembra molto vicino: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6822370. Proveremo l'aggiornamento alla versione più recente di Java 6, in quanto si suppone che l'ultimo bug sia corretto. Grazie per aver postato questo. –

4

pacchetto non sicuri è nativo, un'implementazione dipende da una piattaforma.

brusca cessazione del terzo filo (a livello di piattaforma, excepion non è un problema) che ha acquisito un blocco su carta possono causare tale situazione - stato di blocco è rotto, gli altri due fili sono disabilitate e in attesa di qualcuno per chiamare Unsafe. unpark() (E non succederà mai).

5

Non penso che questo sia ciò che sta accadendo nel tuo caso, ma è possibile scrivere un deadlock con una singola istanza di ConcurrentHashMap, e ha solo bisogno di un thread! Mi ha tenuto bloccato per un bel po '.

Supponiamo che stiate utilizzando uno ConcurrentHashMap<String, Integer> per calcolare un istogramma. Si potrebbe fare qualcosa di simile:

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

Che funziona bene.

Ma diciamo che si decide invece di scrivere in questo modo:

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

Avrete ora una situazione di stallo 1-thread con uno stack del genere:

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 di cui 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 frame stack di callback di eventi tra la chiamata a map.compute e map.putIfAbsent, allora può essere piuttosto difficile rintracciare.