Unravel โบ Java concepts โบ Concurrency
Race conditions and deadlocks
A race condition is a bug where the result depends on the timing of threads (two threads both do "check, then act").
Learn these first
At a glance
Race: two people both see one seat free and sit down at the same time.
- 1Check-then-act: if (!map.containsKey(k)) map.put(k, v); can run twice at onceMake it atomic with putIfAbsent/computeIfAbsent, or lock around both steps.
- 2Read-modify-write: count++ is three stepsUse AtomicInteger.incrementAndGet(), LongAdder or a lock.
- 3Deadlock needs four things: exclusive locks, hold-and-wait, no preemption, and a circular waitA common recipe: thread 1 locks A then B while thread 2 locks B then A.
- 4Prevention: always acquire locks in the same global order; hold locks briefly and never call unknown code while holding one; use tryLock(timeout); prefer one lock or none (immutability, queues)
- Find deadlocks with a thread dump (jstack <pid> or kill -3), which prints "Found one Java-level deadlock" with the threads and locks involved; our thread dump analyzer on this site reads such dumps
- Races rarely show up in small tests
- A synchronized method acquires the object's monitor, stored in the object header (lightweight lock, then inflated to a heavyweight monitor on contention)
public class Transfer { static void move(Account from, Account to, int amount) { synchronized (from) { synchronized (to) { from.balance -= amount; to.balance += amount; } } } static class Account { int balance; }}Read the full explanation (about 2 min)
A race condition is a bug where the result depends on the timing of threads (two threads both do "check, then act"). A deadlock is when threads wait for each other's locks forever.
A mental model
Race: two people both see one seat free and sit down at the same time. Deadlock: two cars in a narrow street, each waiting for the other to back up first.
How it works
Check-then-act: if (!map.containsKey(k)) map.put(k, v); can run twice at once. Make it atomic with putIfAbsent/computeIfAbsent, or lock around both steps.
Read-modify-write: count++ is three steps. Use AtomicInteger.incrementAndGet(), LongAdder or a lock.
Deadlock needs four things: exclusive locks, hold-and-wait, no preemption, and a circular wait. A common recipe: thread 1 locks A then B while thread 2 locks B then A.
Prevention: always acquire locks in the same global order; hold locks briefly and never call unknown code while holding one; use tryLock(timeout); prefer one lock or none (immutability, queues).
Going deeper
Find deadlocks with a thread dump (jstack <pid> or kill -3), which prints "Found one Java-level deadlock" with the threads and locks involved; our thread dump analyzer on this site reads such dumps. ThreadMXBean.findDeadlockedThreads() does the same from code.
Races rarely show up in small tests. Review shared mutable state, stress test with many threads, and use tools (ThreadSanitizer-like detectors, jcstress) for critical code.
Related problems: livelock (threads keep reacting to each other and make no progress), starvation (a thread never gets the lock), and lock contention (too many threads wait on one lock, which looks like low CPU and bad latency).
Under the hood
A synchronized method acquires the object's monitor, stored in the object header (lightweight lock, then inflated to a heavyweight monitor on contention). Blocked threads show as BLOCKED (on object monitor) in dumps with "waiting to lock <0x...>".
Common mistakes
- Locking in inconsistent order.
- Calling a callback or overridable method while holding a lock.
- Locking on a public object or a string literal.
- Believing a synchronized getter and setter make a compound operation safe.
How Unravel recognises it
When you paste code, Unravel looks for these signs of race conditions and deadlocks. Spotting them yourself is a good habit when you read unfamiliar code:
- Types:
ThreadMXBean - Method calls:
findDeadlockedThreads()tryLock()lock()wait() - Language constructs:
nested-syncsynchronized
Part of a learning path
Related concepts
Frequently asked questions
What is Race conditions and deadlocks?
A race condition is a bug where the result depends on the timing of threads (two threads both do "check, then act"). A deadlock is when threads wait for each other's locks forever.
What are the most common mistakes with Race conditions and deadlocks?
Locking in inconsistent order. Calling a callback or overridable method while holding a lock. Locking on a public object or a string literal. Believing a synchronized getter and setter make a compound operation safe.
How can I see Race conditions and deadlocks in real code?
Paste your own Java into Unravel and it will point out where race conditions and deadlocks appears, explain how it works at the detail level you choose, and draw the flow of the code around it.