Возможно ли для ConcurrentHashMap "тупик"? - PullRequest
22 голосов
/ 20 июля 2010

Мы столкнулись со странной проблемой с ConcurrentHashMap, когда два потока, кажется, вызывают put(), а затем ждут вечно внутри метода Unsafe.park(). Снаружи это выглядит как тупик внутри ConcurrentHashMap.

До сих пор мы видели это только один раз.

Может ли кто-нибудь придумать что-нибудь, что может вызвать эти симптомы?

РЕДАКТИРОВАТЬ : Дамп потока для соответствующих потоков здесь:


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

Ответы [ 3 ]

7 голосов
/ 02 декабря 2015

Я не думаю, что это то, что происходит в вашем случае, но можно написать тупик с одним экземпляром ConcurrentHashMap, и для этого нужен только один поток! Удерживал меня на долгое время.

Допустим, вы используете ConcurrentHashMap<String, Integer> для вычисления гистограммы. Вы можете сделать что-то вроде этого:

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

Что отлично работает.

Но скажем, вы решили вместо этого написать это так:

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

Теперь вы получите 1-ниточный тупик со стеком, подобным этому:

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  

В приведенном выше примере легко увидеть, что мы пытаемся изменить карту внутри атомной модификации, что кажется плохой идеей. Однако, если между вызовами map.compute и map.putIfAbsent существует сто кадров стека обратного вызова, то отследить может быть довольно сложно.

4 голосов
/ 21 июля 2010

Пакет Unsafe является нативным, реализация зависит от платформы.

Резкое завершение третьего потока (на уровне платформы, исключение не является проблемой), который получил блокировку на карте, может вызвать такую ​​ситуацию - состояние блокировки нарушено, два других потока отключены и ждут, когда кто-то вызовет Unsafe.unpark () (И этого никогда не произойдет).

4 голосов
/ 20 июля 2010

Возможно, не тот ответ, который вам нужен, но это может быть ошибка JVM.См

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

...