Open-source project
oldratlee/fucking-java-concurrency avatar
oldratlee/fucking-java-concurrency

Eleven Java concurrency bugs you can run in a minute

🎏 Simple showcases of java concurrency problems, seeing 🙈 is believing 🐵

1,215 stars343 forksJavaApache-2.0

At a glance

What is it?
A collection of minimal Java programs, each of which breaks in a way that is instructive: a worker thread that never sees the flag it was told to stop on, a HashMap that spins forever, a long that reads back as a value that was never written, and three kinds of mutual exclusion failure.
Who is it for?
This collection of demos fits a developer who has been burned by a concurrency bug and wants to see the failure rather than read about it, or a team that wants a shared vocabulary of Java memory model symptoms that everybody can run on their own machine. It is the wrong tool for learning why a failure happens, because the demos deliberately come with no explanation, and it is the wrong tool for a codebase that needs a fix, since none of the demos shows the corrected version.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • oldratlee/fucking-java-concurrency1,215 ★ · -1
Star history
Repository2026-10-232026-11-07
oldratlee/fucking-java-concurrency1.2k1.2k

-1 Stars in the last 15 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

A symptom you can watch beats a principle you can recite

The repository the author calls `fucking-java-concurrency` is a set of minimal demos of Java concurrency problems, and the argument for it is made in the README in three steps.

An observed symptom is more intuitive and convincing than a stated concurrency principle. The Java standard library supports threads, and multithreading is heavily used both in the language itself, for example in the garbage collector, and in applications on the server side. And concurrency greatly increases the complexity of program design, analysis and implementation, so unless you fully understand and systematically analyse the concurrent logic, writing code at random means that such a program runs correctly by accident.

That last sentence is the thesis. A program that appears to work is not evidence that it is correct, because a correct-looking result is what a broken program produces right up until the schedule changes. The repository's answer is to make the failure visible on demand.

The method is deliberately minimal. The README states that the demos come with no explanation or discussion, and that all of them are entry-level, and it points readers to concurrency material for the reasoning. Each demo has the same three-part structure, a demo description, a problem statement and a quick run, and each is a small class under `src/main/java/fucking/concurrency/demo/` with a name that says what it does.

The project is Apache-2.0 licensed, the primary language is Java, the badge says Java 8 or later, the default branch is `master`, and the last push was on 2026-10-08. There are no GitHub releases. The documentation is bilingual, with an English README and a Chinese one under `docs/zh-CN/`, and the table of contents is generated by doctoc, with a comment in the file saying not to edit that section but to re-run doctoc instead.

The worker thread that never sees the stop flag

The first demo is the simplest and the one that teaches the most per line of code.

In `NoPublishDemo`, the main thread sets the field `stop` to `true` to signal the task thread, which was started in `main`, that it should exit. The problem statement is the punchline: after the main thread sets `stop` to `true`, the task thread keeps running, that is, it never sees the new value.

What is happening is not that the flag is late, it is that the write is never made visible to the other thread. The field is an ordinary field, and without a happens-before relationship between the write in the main thread and the read in the task thread, the memory model permits the task thread to keep reading the old value indefinitely. A program like this is the textbook case of a program that runs correctly by accident, and it only fails on a particular interleaving, which is why it survives testing and then fails in production.

The demo is worth running on a machine with more than one core, because the failure needs the two threads to actually run in parallel for the symptom to appear reliably.

The repository also asks for contributions in a specific direction. If you hit a concurrency problem in your own development, the README asks you to submit an issue, or to fork the repository and open a pull request. The intent is to accumulate a catalogue of symptoms, not to build a framework, and the topic list on the repository reflects that: concurrency gotchas, concurrency issues, deadlock, multi-threading, parallelism, synchronisation and thread safety.

Torn reads: a long that was never written, and states that never existed

Three of the demos are about values that come back wrong rather than values that never arrive.

Reading a long variable yields an invalid value is the classic torn read. A `long` is wider than the atomic unit a single read is guaranteed to cover, so a thread that writes a 64-bit value while another thread reads it can observe a value that is a mixture of the old and the new one. The demo makes that visible: the value the reader gets is one that was never written by anyone.

Reading combined state yields an invalid combination is the same idea applied to a group of fields. Programs often need to track several related pieces of state, and the demo shows a read of those fields together producing a combination that the program never wrote and that no single thread ever held. A thread-safe field is not a thread-safe object, and this is the demo that shows why.

Consecutive reads of the same field see different values makes the same point from the other end. Two reads in a row of one field return two different values, so the reader cannot even treat its own read as a consistent snapshot.

Taken together, the three demos separate three things that are often conflated. A value that is never updated, a value that is updated but not seen, and a value that is seen half-updated are three different failures with three different fixes, and a team that has only met one of them tends to reach for the wrong fix. The README's decision not to explain them is the trade: the symptom is unambiguous, and the explanation has to come from elsewhere.

The HashMap that spins forever

The second demo is the one that looks like a hang rather than a wrong answer, and it is a good demonstration because the symptom is so distinctive.

In `HashMapHangDemo`, the main thread starts two task threads that `put` into the `HashMap`, while itself repeatedly performing `get`. The problem statement is that the main thread blocks, that is, the output stops, which means the `HashMap` has entered an infinite loop. The README notes that this problem has been explained in many places, and points at the demo as the reproduction.

The shape of the failure is what makes it memorable. A `HashMap` that is not safe for concurrent use can be corrupted into a structure that contains a cycle, and a lookup that walks into that cycle never returns. The program is not deadlocked, in the sense that no thread is waiting for a lock; the threads are all runnable and one of them is spinning. That distinction is worth seeing in the flesh, because the two problems feel identical from the outside, both look like the program stopped, and they have completely different diagnoses.

It is also a warning about a specific habit. A `HashMap` is a fine choice for a map that one thread owns, and it is a poor choice for a map that several threads write to, and the difference is invisible in the type. The alternative the demo is really about is the concurrent map, and the fact that the standard library has one is the reason the failure is avoidable.

Deadlock, livelock, and the pool that waits on itself

Three demos cover the failure modes where threads are all alive and nothing happens, and the differences between them are the point.

Deadlock caused by symmetric locks is the textbook shape: two threads take the two locks in opposite orders and wait for each other forever. Livelock caused by reentrant locks is the subtler one, where the threads are not blocked, they are running, and they keep undoing each other's progress, so the program is busy and accomplishing nothing. A liveness bug that burns a core is much harder to spot than one that stops, which is the argument for having a demo of it.

Cyclic thread pool deadlock is the one that catches people in production, because there is no explicit lock anywhere in the code. A task submitted to a pool waits for another task that is queued behind it in the same pool, and with a bounded pool and a saturated queue the pool waits for itself. Nothing in the code looks wrong, and the symptom is a service that stops responding under load.

Two more demos close the set. Synchronization on mutable fields is about locking on an object whose identity or contents can change, so the lock is not the lock you think it is. And instruction reordering causes incorrect reads of non-final fields, which is the low-level cousin of the visibility demo and the reason that `final` has memory semantics in Java rather than only a stylistic one.

There is also a demo on unsynchronized concurrent counting giving wrong results, which is the one everybody has written by accident at least once, and the one that makes the case for the atomic classes the most obvious.

Running one demo takes one command

The quick run section of every demo is a single Maven invocation, and the wrapper is included in the repository, with `mvnw` and `mvnw.cmd` at the root and a `.mvn/` directory beside them.

bash
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.NoPublishDemo

The pattern is the same for every demo, with only the class name changing. The `HashMap` one is:

bash
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.HashMapHangDemo

And the deadlock one would be the same shape with its own class name. The classes live in `src/main/java/fucking/concurrency/demo/`, which is worth noting because the package name is a little alarming to see in a stack trace, and it is the one piece of this repository that is deliberately blunt about what it is for.

The build is a plain Maven project with a `pom.xml` at the root, and the repository also carries an `AGENTS.md`, an `.editorconfig` and a `docs/` directory that holds the Chinese documentation and images, including the dining philosophers diagram that appears in the README.

One practical warning for anyone wiring this into a pipeline. Several of these programs are supposed to hang. The HashMap demo blocks, the deadlock demos never terminate, and the livelock demo spins. A job that runs them has to give them a timeout, because a demo that is supposed to fail is indistinguishable from a hung test runner.

Where this set of demos is the wrong tool

The limits are the direct consequence of the stated method, and a reader should know them before spending an afternoon on it.

The first is that the demos teach symptoms and not fixes. The README is explicit that there is no explanation or discussion, so a developer who runs the torn read demo learns what it looks like and has to work out the cause from the specification or from other material. That is a deliberate trade, and it works well for a developer who already knows the theory and wants to see the failure, and poorly for one who has just met the Java memory model.

The second is the era. The badge says Java 8 or later, and the concurrency primitives of that generation are the ones on display, which means the collection says nothing about the structured concurrency and virtual threads work that has arrived since. A team evaluating concurrency for a modern codebase will find the vocabulary still useful and the conclusions dated.

The third is the operational risk of running the demos, which is not a criticism of the project but a property of it. A program designed to deadlock has to be killed, so the repository is a thing to run by hand, with a terminal you can interrupt, rather than something to put in a build that expects every process to exit.

The alternatives are of two kinds. The formal one is the specification: the Java language specification's chapter on threads and the memory model, or the concurrency material the README points the reader to, which gives the reasoning that the demos deliberately leave out. The practical one is to stop writing concurrent code by hand and use a structured model, whether that is an executor that owns the thread pool, a message passing design, or whatever the language now offers for scoping tasks. The difference in approach is that this repository proves the failures are real, and the specification and the structured alternatives are what tell you not to write the code that fails.

Editorial conclusion

This collection of demos fits a developer who has been burned by a concurrency bug and wants to see the failure rather than read about it, or a team that wants a shared vocabulary of Java memory model symptoms that everybody can run on their own machine. It is the wrong tool for learning why a failure happens, because the demos deliberately come with no explanation, and it is the wrong tool for a codebase that needs a fix, since none of the demos shows the corrected version. Before running it in continuous integration, remember that some of these programs hang by design, that the HashMap demo blocks forever and the deadlock demos never terminate, so a job that runs them needs a timeout rather than an expectation of a clean exit.

Frequently asked questions

What is the oldratlee/fucking-java-concurrency repository?

It is a collection of minimal Java programs that each reproduce one concurrency problem, every demo with a description, a problem statement and a run command. The argument in the README is that an observed symptom convinces more than a stated principle, and that code written without systematic analysis runs correctly by accident.

How do I run a specific concurrency demo?

With the bundled Maven wrapper, for example `./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.NoPublishDemo`. The demo classes live under `src/main/java/fucking/concurrency/demo/`, and each has its own class name in the README.

Which concurrency problems does the project demonstrate?

Eleven: unsynchronized updates not visible to other threads, an infinite loop in HashMap, reading combined state yielding an invalid combination, reading a long yielding an invalid value, consecutive reads of the same field seeing different values, unsynchronized concurrent counting giving wrong results, synchronization on mutable fields, deadlock from symmetric locks, livelock from reentrant locks, instruction reordering causing incorrect reads of non-final fields, and cyclic thread pool deadlock.

Does the project show how to fix the concurrency problems?

No. The README states that the demos come with no explanation or discussion and that all of them are entry-level, and it directs readers to concurrency material for the reasoning. The project asks for contributions in the other direction, as new problem demonstrations through an issue or a pull request.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. oldratlee/fucking-java-concurrency on GitHub
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/oldratlee-fucking-java-concurrency.svg)](https://hysenlabs.com/projects/oldratlee-fucking-java-concurrency)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/oldratlee-fucking-java-concurrency.svg)](https://hysenlabs.com/projects/oldratlee-fucking-java-concurrency)

Was this useful?