2010-07-20 15 views
21

Hemos encontrado un problema extraño con ConcurrentHashMap, donde dos hilos parecen llamar al put(), y luego esperar para siempre dentro del método Unsafe.park(). Desde el exterior, parece un punto muerto dentro de ConcurrentHashMap.¿Es posible que ConcurrentHashMap esté en "punto muerto"?

Solo hemos visto esto una vez hasta ahora.

¿Alguien puede pensar en algo que pueda causar estos síntomas?

EDITAR: El vertedero de hilo para los hilos pertinentes está aquí:

 

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

¿Tiene el volcado de hilo? –

+0

@John W .: buen punto. Lo publicaré tan pronto como pueda obtenerlo del servidor. –

+0

¿Hay alguna otra parte del volcado de subprocesos que muestre qué hilo realmente posee el bloqueo? Estos hilos están esperando a ser adquiridos. Averiguar lo que están esperando puede ayudar. –

Respuesta

4

Quizás no sea la respuesta que desea, pero esto puede ser un error de JVM. Ver

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

+0

Este error relacionado también se ve muy cerca: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6822370. Intentaremos actualizar a la última versión de Java 6, ya que supuestamente el último error está solucionado. Gracias por publicar esto. –

4

paquete inseguro es nativa, una implementación depende de una plataforma.

La terminación abrupta del tercer hilo (a nivel de plataforma, excepto no es un problema) que adquirió un bloqueo en el mapa puede causar tal situación - estado de bloqueo roto, otros dos hilos están deshabilitados y esperando que alguien llame a inseguro. unpark() (Y eso nunca sucederá).

5

No creo que esto sea lo que está sucediendo en su caso, pero es posible escribir un punto muerto con una sola instancia de ConcurrentHashMap, ¡y solo necesita un hilo! Me mantuvo atrapado por un buen tiempo.

Digamos que está utilizando un ConcurrentHashMap<String, Integer> para calcular un histograma. Puede hacer algo como esto:

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

Que funciona bien.

Pero digamos que usted decide en lugar de escribirlo así:

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

Ahora obtendrá un punto muerto 1-hilo con una pila de esta manera:

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 

En el ejemplo anterior, Es fácil ver que estamos intentando modificar el mapa dentro de una modificación atómica, lo cual parece una mala idea. Sin embargo, si hay cientos de marcos de pila de devoluciones de llamada de eventos entre la llamada a map.compute y map.putIfAbsent, entonces puede ser bastante difícil de localizar.

Cuestiones relacionadas