π Simple showcases of java concurrency problems, seeing π is believing π΅
See the codeπ English Documentation | π δΈζζζ‘£
Minimal demos of Java concurrency problems β seeing π is believing π΅.
Java standard library supports threads, and multithreading is
heavily used both in the language itself (e.g. GC) and in
applications (server side).Hit a concurrency problem in your own development? Please share it β submit an issue or fork the repo and open a pull request! π
HashMap
long variable yields an invalid value
Demo class NoPublishDemo.
The main thread sets the field stop to true to signal the task
thread (started in main) to exit.
After the main thread sets stop to true, the task thread keeps
running β i.e. it never sees the new value.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.NoPublishDemo
HashMapThis problem has been explained in many places.
The Demo class HashMapHangDemo
can reproduce this problem.
The main thread starts two task threads that put into the HashMap,
while itself repeatedly performing get.
The main thread blocks (output stops), i.e. the HashMap has entered
an infinite loop.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.HashMapHangDemo
Programs often need to track several related pieces of state (a
POJO, a few ints, etc.).
Multi-state read/write code is frequently left unsynchronized, and whoever writes it naturally overlooks thread safety.
An invalid combination is a combination of values that the program never actually wrote.
The main thread modifies multiple states. For convenience of checking,
every write keeps a fixed relationship: the second state is always
twice the first. A task thread reads the states.
Demo class InvalidCombinationStateDemo.
The task thread reads a second state that is not twice the first β a combination that was never written.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.InvalidCombinationStateDemo
long variable yields an invalid valueAn invalid value is one that was never written.
Reads and writes of non-volatile long and double are not guaranteed
to be atomic.
per the Java Language Specification (Β§17.7), a single read/write of a 64-bit value may be treated as two separate 32-bit accesses. On 32-bit JVMs this is necessarily split into two 4-byte operations; on 64-bit JVMs it is typically done atomically in practice, but the specification still does not guarantee atomicity.
Demo class InvalidLongDemo.
The main thread modifies the long variable. For convenience of
checking, the upper and lower 4 bytes of every value written are
identical. A task thread reads the long.
The task thread reads a long whose upper and lower 4 bytes differ β
a value that was never written.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.InvalidLongDemo
Demo class InconsistentReadDemo.
The main thread increments the plain count field in a tight loop.
A task thread performs two consecutive reads of count and reports
each time the two values differ.
Two consecutive reads of the same non-volatile field by the same thread return different values. For a plain field, each read may observe a different write, so a thread must not assume that two reads see the same value.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.InconsistentReadDemo
Demo class WrongCounterDemo.
The main thread starts two task threads that increment a shared counter concurrently, then checks the final result.
The final count is incorrect.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.WrongCounterDemo
Synchronizing on a volatile field is common, and whoever writes it
naturally assumes this is safe and correct.
# For problem analysis, see the article
Synchronization on mutable fields.
Demo class SynchronizationOnMutableFieldDemo.
The main thread starts two task threads that call addListener, then
checks the final result.
The final listener count is incorrect.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.SynchronizationOnMutableFieldDemo
# For problem analysis, see the article Synchronization on mutable fields
Demo class SymmetricLockDeadlockDemo.
The main thread starts two task threads, each of which takes the two locks in the opposite order from the other.
The task threads deadlock.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.SymmetricLockDeadlockDemo
# For a problem description, see the paragraph about livelocks in the article
Demo class ReentrantLockLivelockDemo.
Two task threads try to acquire the lock the other thread holds while holding their own lock.
The threads release their own lock and immediately re-acquire it, denying the other thread a chance to acquire both locks. The threads keep executing: an attempt wastes its work, and overall they make almost no actual progress. This is a livelock.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.ReentrantLockLivelockDemo
Demo class FinalInitialDemo.
The writer thread calls the class constructor while the reader thread reads the class's non-final fields.
When the constructor runs, instruction reordering can move the stores of non-final fields outside it, so the reader thread may observe the fields' default values instead of the values set by the constructor. (Reordering is not guaranteed; it requires specific hardware and JVM conditions.)
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.FinalInitialDemo
Demo class CyclicThreadPoolDeadLockDemo.
This example demonstrates the deadlock caused by cyclic dependencies
between tasks when using thread pools, and how to avoid it with
CompletableFuture.
In the badCase, two thread pools, pool1 and pool2, submit tasks
to each other, forming a cyclic dependency.
When the pools' threads are exhausted, every running task waits for
another task to complete, and the pools deadlock.
The goodCase replaces this with asynchronous chained calls using
CompletableFuture, so no pool thread blocks.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.CyclicThreadPoolDeadLockDemo
5,694 followers Β· starred May 2018
1,585 followers Β· starred Dec 2020
0 followers Β· starred Sep 2019
32 followers Β· starred Feb 2025
π Simple showcases of java concurrency problems, seeing π is believing π΅
See the codeπ English Documentation | π δΈζζζ‘£
Minimal demos of Java concurrency problems β seeing π is believing π΅.
Java standard library supports threads, and multithreading is
heavily used both in the language itself (e.g. GC) and in
applications (server side).Hit a concurrency problem in your own development? Please share it β submit an issue or fork the repo and open a pull request! π
HashMap
long variable yields an invalid value
Demo class NoPublishDemo.
The main thread sets the field stop to true to signal the task
thread (started in main) to exit.
After the main thread sets stop to true, the task thread keeps
running β i.e. it never sees the new value.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.NoPublishDemo
HashMapThis problem has been explained in many places.
The Demo class HashMapHangDemo
can reproduce this problem.
The main thread starts two task threads that put into the HashMap,
while itself repeatedly performing get.
The main thread blocks (output stops), i.e. the HashMap has entered
an infinite loop.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.HashMapHangDemo
Programs often need to track several related pieces of state (a
POJO, a few ints, etc.).
Multi-state read/write code is frequently left unsynchronized, and whoever writes it naturally overlooks thread safety.
An invalid combination is a combination of values that the program never actually wrote.
The main thread modifies multiple states. For convenience of checking,
every write keeps a fixed relationship: the second state is always
twice the first. A task thread reads the states.
Demo class InvalidCombinationStateDemo.
The task thread reads a second state that is not twice the first β a combination that was never written.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.InvalidCombinationStateDemo
long variable yields an invalid valueAn invalid value is one that was never written.
Reads and writes of non-volatile long and double are not guaranteed
to be atomic.
per the Java Language Specification (Β§17.7), a single read/write of a 64-bit value may be treated as two separate 32-bit accesses. On 32-bit JVMs this is necessarily split into two 4-byte operations; on 64-bit JVMs it is typically done atomically in practice, but the specification still does not guarantee atomicity.
Demo class InvalidLongDemo.
The main thread modifies the long variable. For convenience of
checking, the upper and lower 4 bytes of every value written are
identical. A task thread reads the long.
The task thread reads a long whose upper and lower 4 bytes differ β
a value that was never written.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.InvalidLongDemo
Demo class InconsistentReadDemo.
The main thread increments the plain count field in a tight loop.
A task thread performs two consecutive reads of count and reports
each time the two values differ.
Two consecutive reads of the same non-volatile field by the same thread return different values. For a plain field, each read may observe a different write, so a thread must not assume that two reads see the same value.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.InconsistentReadDemo
Demo class WrongCounterDemo.
The main thread starts two task threads that increment a shared counter concurrently, then checks the final result.
The final count is incorrect.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.WrongCounterDemo
Synchronizing on a volatile field is common, and whoever writes it
naturally assumes this is safe and correct.
# For problem analysis, see the article
Synchronization on mutable fields.
Demo class SynchronizationOnMutableFieldDemo.
The main thread starts two task threads that call addListener, then
checks the final result.
The final listener count is incorrect.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.SynchronizationOnMutableFieldDemo
# For problem analysis, see the article Synchronization on mutable fields
Demo class SymmetricLockDeadlockDemo.
The main thread starts two task threads, each of which takes the two locks in the opposite order from the other.
The task threads deadlock.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.SymmetricLockDeadlockDemo
# For a problem description, see the paragraph about livelocks in the article
Demo class ReentrantLockLivelockDemo.
Two task threads try to acquire the lock the other thread holds while holding their own lock.
The threads release their own lock and immediately re-acquire it, denying the other thread a chance to acquire both locks. The threads keep executing: an attempt wastes its work, and overall they make almost no actual progress. This is a livelock.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.ReentrantLockLivelockDemo
Demo class FinalInitialDemo.
The writer thread calls the class constructor while the reader thread reads the class's non-final fields.
When the constructor runs, instruction reordering can move the stores of non-final fields outside it, so the reader thread may observe the fields' default values instead of the values set by the constructor. (Reordering is not guaranteed; it requires specific hardware and JVM conditions.)
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.FinalInitialDemo
Demo class CyclicThreadPoolDeadLockDemo.
This example demonstrates the deadlock caused by cyclic dependencies
between tasks when using thread pools, and how to avoid it with
CompletableFuture.
In the badCase, two thread pools, pool1 and pool2, submit tasks
to each other, forming a cyclic dependency.
When the pools' threads are exhausted, every running task waits for
another task to complete, and the pools deadlock.
The goodCase replaces this with asynchronous chained calls using
CompletableFuture, so no pool thread blocks.
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.CyclicThreadPoolDeadLockDemo
5,694 followers Β· starred May 2018
1,585 followers Β· starred Dec 2020
0 followers Β· starred Sep 2019
32 followers Β· starred Feb 2025