2010-07-20 10 views
21

Nous avons rencontré un problème étrange avec ConcurrentHashMap, où deux threads semble appeler put(), puis en attente pour toujours à l'intérieur de la méthode Unsafe.park(). De l'extérieur, cela ressemble à une impasse à l'intérieur ConcurrentHashMap.Est-il possible que ConcurrentHashMap devienne "deadlock"?

Nous n'avons vu cela que jusqu'à présent. Quelqu'un peut-il penser à quelque chose qui pourrait causer ces symptômes?

EDIT: Le vidage de fil pour les fils concernés est ici:

 

"[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

Avez-vous le vidage de fil? –

+0

@ John W .: bon point. Je l'afficherai dès que je pourrai l'enlever du serveur. –

+0

Y a-t-il une autre partie de la sauvegarde de thread qui montre quel thread possède réellement le verrou? Ces threads attendent simplement d'acquérir. Découvrir ce qu'ils attendent peut aider. –

Répondre

4

Peut-être pas la réponse que vous voulez, mais cela peut être un bug JVM. Voir

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

+0

Ce bogue connexe est également très proche: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6822370. Nous allons essayer de mettre à jour vers la dernière version de Java 6, car ce dernier bug est censé être corrigé. Merci d'avoir posté cela. –

4

Package insalubre est natif, une mise en œuvre dépend d'une plate-forme. La fin abrupte du troisième thread (au niveau de la plate-forme, l'exception n'est pas un problème) qui a acquis un verrou sur la carte peut provoquer une telle situation - l'état de verrouillage est cassé, deux autres threads sont désactivés et attendent quelqu'un pour appeler Unsafe. unpark() (Et cela n'arrivera jamais).

5

Je ne pense pas que ce soit ce qui se passe dans votre cas, mais il est possible d'écrire une impasse avec une seule instance de ConcurrentHashMap, et il ne nécessite qu'un seul fil! Je me suis coincé pendant un bon moment.

Imaginons que vous utilisiez un ConcurrentHashMap<String, Integer> pour calculer un histogramme. Vous pourriez faire quelque chose comme ceci:

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

Ce qui fonctionne très bien.

Mais disons que vous décidez au lieu de l'écrire comme ceci:

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

Vous allez maintenant obtenir une impasse 1-fil avec une pile comme ceci:

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 

Dans l'exemple ci-dessus, il est facile de voir que nous essayons de modifier la carte à l'intérieur d'une modification atomique, ce qui semble être une mauvaise idée. Toutefois, s'il existe une centaine de trames de pile de rappels d'événements entre l'appel à map.compute et map.putIfAbsent, il peut alors être assez difficile à traquer.