2010-07-20 9 views
21

Wir sind auf ein seltsames Problem mit ConcurrentHashMap gestoßen, wo zwei Threads zu rufen scheint put(), und dann für immer innerhalb der Methode Unsafe.park() warten. Von außen sieht es wie ein Deadlock innerhalb ConcurrentHashMap aus.Kann ConcurrentHashMap "Deadlock"?

Wir haben das bisher nur einmal gesehen.

Kann jemand an etwas denken, das diese Symptome verursachen könnte?

EDIT: Das Thread-Dump für die relevanten Themen ist hier:

 

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

Haben Sie den Thread Dump? –

+0

@ John W .: guter Punkt. Ich werde es veröffentlichen, sobald ich es vom Server bekomme. –

+0

Gibt es einen anderen Teil des Thread-Dumps, der anzeigt, welcher Thread tatsächlich die Sperre besitzt? Diese Threads warten nur darauf, erworben zu werden. Herauszufinden, worauf sie warten, kann helfen. –

Antwort

4

Vielleicht nicht die Antwort, die Sie wollen, aber dies kann ein JVM Fehler sein. Siehe

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

+0

Dieser verwandte Fehler sieht auch ziemlich nah: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6822370. Wir werden versuchen, auf die neueste Version von Java 6 zu aktualisieren, da der letztere Fehler behoben sein sollte. Danke für das Posten. –

4

Paket Unsafe stammt, eine Implementierung hängt von einer Plattform.

Abrupt Beendigung des dritten Threads (auf Plattform-Ebene, Ausnahme ist kein Problem), die eine Sperre auf Karte erworben kann solche Situation verursachen - Zustand der Sperre ist kaputt, zwei andere Threads sind deaktiviert und warten auf jemanden Unsichere aufrufen. unpark() (Und das wird nie passieren).

5

Ich glaube nicht, dass das in Ihrem Fall passiert, aber es ist möglich, einen Deadlock mit einer einzigen Instanz von ConcurrentHashMap zu schreiben, und es braucht nur einen Thread! Hielt mich eine ganze Weile fest.

Nehmen wir an, Sie verwenden ein ConcurrentHashMap<String, Integer>, um ein Histogramm zu berechnen. Sie könnten etwas tun:

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

Was funktioniert gut.

Aber lassen Sie uns sagen, Sie entscheiden, anstatt es wie folgt zu schreiben:

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

Sie werden nun ein 1-Thread-Deadlock mit einem Stapel wie diese:

Im Beispiel oben
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 

, Es ist leicht zu sehen, dass wir versuchen, die Karte innerhalb einer atomaren Modifikation zu modifizieren, was wie eine schlechte Idee erscheint. Wenn es jedoch zwischen dem Aufruf von map.compute und map.putIfAbsent einhundert Stack-Frames von Ereignisrückrufen gibt, kann es ziemlich schwierig sein, aufzuspüren.