Ответ 1
Возможно, не ответ, который вы хотите, но это может быть ошибка JVM. См
Мы столкнулись с странной проблемой с ConcurrentHashMap
, где два потока, как представляется, вызывают put()
, а затем всегда ждут внутри метода Unsafe.park()
. Снаружи он выглядит как тупик внутри ConcurrentHashMap
.
Мы видели это только однажды.
Может ли кто-нибудь подумать о чем-нибудь, что может вызвать эти симптомы?
EDIT: дамп потоков для соответствующих потоков:
"[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]
Возможно, не ответ, который вы хотите, но это может быть ошибка JVM. См
Я не думаю, что это то, что происходит в вашем случае, но можно написать тупик с единственным экземпляром 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
, то это может быть довольно сложно отследить.
Пакет Unsafe является родным, реализация зависит от платформы.
Резкое прекращение третьего потока (на уровне платформы, исключение не является проблемой), которое приобрело блокировку на карте, может привести к такой ситуации - состояние блокировки сломано, два других потока отключены и ждут вызова кого-то Unsafe.unpark() (И этого никогда не произойдет).