Concurrency is hard. Java has no exception. In this post and possible future posts, I will record traps and pitfalls, I experienced or heard, in Java.
Nested write in ConcurrentHashMap.compute
could deadlock
ConcurrentHashMap
uses bucket level lock in write operations (e.g. put
, compute
) to protect bucket nodes. If nested writing key falls to the same bucket ConcurrentHashMap.compute
is serving, then it deadlocks. The javadoc of ConcurrentHashMap.compute
and its siblings warn this.
Some attempted update operations on this map by other threads may be blocked while computation is in progress, so the computation should be short and simple, and must not attempt to update any other mappings of this Map.
I encountered this once in production code and the “update” is shadowed by ServiceLoader
.
There are others encountering this.
- JDK-8062841: ConcurrentHashMap.computeIfAbsent stuck in an endless loop
- Deadlock due to ConcurrentHashMap.compute in PrometheusMeterRegistry
- Avoid Recursion in ConcurrentHashMap.computeIfAbsent()
CompletableFuture.complete
will run non-async computations if it completes the future
Actions supplied for dependent completions of non-async methods may be performed by the thread that completes the current CompletableFuture, or by any other caller of a completion method.
I think it is not a good design, it makes CompletableFuture.complete
vulnerable to CompletableFuture.then
, CompletableFuture.when
and CompletableFuture.handle
. I did see code in production utilize this subtlety to build strong happen-before relation between when
and code after complete
.