What this topic covers
11 subtopics- Creating threads6
- Queues and executors6
- Thread safety6
- Memory visibility5
- Basics4
- Locks4
- Race conditions4
- Atomics3
- Performance3
- Async2
- Coordination2
- Beginner 15
- Intermediate 15
- Advanced 15
About this topic
Concurrency bugs are some of the hardest to find, and interviewers love asking what a small threaded program prints. Every program here was compiled and run – and written so that its output is deterministic, using join(), locks, latches or queues – while questions about what could happen under any interleaving were answered by trying every interleaving.
Free preview
Sample questions
Try each one first, then open the answer for the explanation and why the other options are wrong.
What does this print?
public class Main {
public static void main(String[] args) {
Thread t = new Thread(() -> System.out.println(Thread.currentThread().getName()), "worker");
t.run();
}
}
Show answer and explanation
Explanation
run() is just an ordinary method call on the current thread. Only start() creates a new thread that then calls run().
Why the other options are wrong
- AThe lambda runs.
- CThe thread was named, and never started.
- DThat needs t.start().
A shared counter starts at 0. Two threads each run count++ twice without synchronisation (each increment is a load and a store). What is the smallest possible final value?
The answer is a number – work it out, then open the solution.
Show answer and explanation
Explanation
Worst case: T1 loads 0 and stalls; T2 completes one increment (x = 1); T1 stores 1; T2 loads 1 and stalls; T1 completes its second increment (x = 2); T2 stores 2. So 2 is the minimum, not 1. With only one increment per thread the minimum would be 1; with two or more each, it is 2.
x = y = 0. Thread 1 runs x = 1; r1 = y; and thread 2 runs y = 1; r2 = x;. Assuming sequential consistency (some interleaving of the four statements), which outcome is impossible?
Show answer and explanation
Explanation
In every interleaving one of the writes comes before both reads, so at least one read sees 1. But real x86 and ARM CPUs can produce (0, 0), because each write can sit in a store buffer while the read goes ahead – one reason why plain variables are not enough for synchronisation.
Why the other options are wrong
- AThread 2 runs fully first.
- CThread 1 runs fully first.
- DBoth writes before both reads.