Advanced Concurrency
Thread safety, concurrent collections, deadlock, livelock and starvation
Advanced concurrency in Java is about keeping multi-threaded systems correct, diagnosable, and scalable under real contention. At this level the conversation shifts from one lock or one keyword to thread safety strategy, safe publication, concurrent collections, progress guarantees, and failure modes such as deadlock, livelock, and starvation.
1. Definition
What is advanced concurrency?
It is the design of concurrent systems beyond basic thread creation and single-lock protection.
The central questions become:
- how is shared state owned?
- how is state published safely?
- how do many threads coordinate without collapse?
- how do you diagnose lack of progress?
Why this matters
Most painful concurrency bugs are not just one missing keyword.
They usually come from:
- inconsistent ownership
- hidden shared mutability
- incorrect publication
- wrong collection choice
- lock ordering mistakes
- progress failures under load
What should a strong answer include?
A strong answer mentions:
- thread safety strategies
- immutability
- safe publication
- concurrent collections
- atomic classes
LongAdder- deadlock
- livelock
- starvation
- backpressure and queue-based design where relevant
2. Core Concepts
2.1 Thread safety strategies
Thread safety can be achieved in several ways:
- immutability
- thread confinement
- safe publication
- synchronization
- lock-free or low-lock coordination
Immutability is often the cheapest in mental overhead.
If state cannot change, readers do not need coordination after safe publication.
Thread confinement is also strong.
If only one thread owns a stateful component, many synchronization problems disappear.
2.1.1 Keywords and contracts you should state explicitly here
The key terms for this topic are:
- thread safety — correct behavior under concurrent use
- immutability — state cannot change after construction
- thread confinement — state is used by only one thread
- safe publication — other threads observe a fully initialized object through a valid publication edge
volatile— visibility-oriented field modifier with ordering effectsConcurrentHashMap— concurrent shared map implementationCopyOnWriteArrayList— read-optimized list with copy-on-write mutation costBlockingQueue— producer-consumer handoff with blocking semanticsConcurrentLinkedQueue— non-blocking queue without built-in backpressureAtomicInteger/AtomicReference— atomic primitives for small state transitionsLongAdder— contention-friendly counter for heavy write scenarios- deadlock — cyclic waiting with no progress
- livelock — active threads that still make no useful progress
- starvation — some work rarely gets access because others dominate resources
2.2 Safe publication
Constructing an object safely is not enough.
Other threads must also observe it through a valid publication edge.
Examples include:
- final-field semantics after correct construction
- static initialization
- volatile write
- lock release
- concurrent collection insertion
- executor handoff
Without safe publication, another thread may see stale references or partially initialized state.
2.3 Concurrent collections
Concurrent collections encode specific coordination strategies.
ConcurrentHashMap is a strong default for shared maps.
CopyOnWriteArrayList is useful for read-mostly structures like listener registries.
BlockingQueue is excellent for producer-consumer pipelines because it expresses:
- handoff
- waiting
- backpressure
ConcurrentLinkedQueue gives non-blocking FIFO behavior, but not backpressure.
Choosing among them is really choosing consistency and contention behavior.
2.4 Progress failures
Correctness is not enough.
You also need progress.
Deadlock means threads wait forever in a cycle.
Livelock means threads remain active but do not complete useful work.
Starvation means some task or thread gets too little access to needed resources.
These are common production incident patterns, not just textbook concepts.
3. Practical Usage
Prefer less sharing first
Before reaching for clever locking, try to reduce shared mutable state.
Often the simplest scalable design uses:
- immutable values
- message passing
- queue-based handoff
- shard ownership
- per-thread or per-component confinement
Concurrent collection fit
Use ConcurrentHashMap when many threads share a map.
But remember compound actions still matter.
containsKey() then put() is not atomic.
computeIfAbsent() may be the correct operation.
Use CopyOnWriteArrayList only when writes are rare.
Every write copies the full backing array.
Use BlockingQueue when you need coordination plus pressure.
Atomic primitives with realism
Atomic types are great for:
- flags
- small state machines
- simple counters
Under heavy write contention, LongAdder often scales better than AtomicLong.
But atomics do not magically protect multi-field invariants.
If consistency spans several variables, stronger coordination is still needed.
4. Code Examples
Example 1: `ConcurrentHashMap` plus `LongAdder`
class MetricsRegistry {
private final ConcurrentHashMap<String, LongAdder> counters = new ConcurrentHashMap<>();
void increment(String name) {
counters.computeIfAbsent(name, key -> new LongAdder()).increment();
}
long current(String name) {
LongAdder adder = counters.get(name);
return adder == null ? 0L : adder.sum();
}
}
Key points:
- concurrent map for shared access
LongAdderfor contention-friendly increments- atomic map initialization via
computeIfAbsent
Example 2: Queue-based work handoff
class WorkPipeline {
private final BlockingQueue<Job> queue = new ArrayBlockingQueue<>(1000);
void submit(Job job) throws InterruptedException {
queue.put(job);
}
void workerLoop() {
while (!Thread.currentThread().isInterrupted()) {
try {
Job job = queue.take();
process(job);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
private void process(Job job) {}
}
Key points:
- queue encodes waiting and backpressure
- simpler than hand-rolled coordination in many pipelines
Example 3: Deadlock avoidance by lock ordering or timed locking
boolean transfer(Account from, Account to, long amount,
Lock first, Lock second) throws InterruptedException {
if (!first.tryLock(50, TimeUnit.MILLISECONDS)) {
return false;
}
try {
if (!second.tryLock(50, TimeUnit.MILLISECONDS)) {
return false;
}
try {
from.debit(amount);
to.credit(amount);
return true;
} finally {
second.unlock();
}
} finally {
first.unlock();
}
}
Key points:
- time-bounded acquisition can reduce permanent waiting risk
- ordering and timeout are both design tools
5. Trade-offs
| Choice | Advantage | Cost or risk |
|---|---|---|
| Immutability | Lowest coordination complexity | Requires different data-flow style |
| Thread confinement | Simple reasoning within an ownership boundary | Less flexible sharing |
ConcurrentHashMap |
Strong default for shared maps | Compound actions still need thought |
CopyOnWriteArrayList |
Excellent for read-mostly workloads | Very expensive writes |
BlockingQueue |
Encodes handoff and backpressure | Blocking semantics may not fit every case |
| Atomics | Great for small state transitions | Weak for multi-field invariants |
LongAdder |
Better counter scaling under contention | Less precise instant semantics than single-value CAS intuition |
Practical trade-off analysis
Advanced concurrency is mostly about choosing the lowest-complexity design that still scales.
That usually means:
- reduce sharing first
- choose the collection that matches the access pattern
- avoid proving complicated lock protocols if a queue or immutable snapshot can replace them
6. Common Mistakes
Mistake 1: Assuming thread safety comes only from locks
Immutability and confinement can be stronger and simpler.
Correct approach:
- discuss multiple strategies, not only mutexes
Mistake 2: Ignoring safe publication
A safely built object still needs a valid publication edge.
Correct approach:
- explain how the reference becomes visible to other threads
Mistake 3: Using `ConcurrentHashMap` but performing non-atomic compound actions around it
The collection is concurrent.
Your multi-step logic may still race.
Correct approach:
- use atomic methods like
computeIfAbsentwhen needed
Mistake 4: Using `CopyOnWriteArrayList` on write-heavy paths
Every mutation copies the array.
Correct approach:
- reserve it for read-mostly structures
Mistake 5: Treating atomics as a universal concurrency solution
They work best for narrow state transitions.
Correct approach:
- use stronger coordination when several fields form one invariant
Mistake 6: Failing to distinguish deadlock, livelock, and starvation
These are different progress failures.
Correct approach:
- define them precisely and discuss concrete symptoms
7. Deep Dive
7.1 Why safe publication is central
Many developers focus on mutation control.
But publication is equally important.
If a reference escapes incorrectly, other threads may observe a partially initialized object even when the constructor looked fine.
That is why safe publication is a foundational concurrency topic.
7.2 Collection choice is strategy choice
Choosing a concurrent collection is choosing a coordination model.
For example:
BlockingQueuechooses pressure-aware handoffConcurrentLinkedQueuechooses lock-free progress without backpressureCopyOnWriteArrayListchooses read-speed over write-cost
Senior answers make that strategy explicit.
7.3 Progress bugs as production incidents
Deadlock often appears as stuck threads with cyclic waiting.
Livelock appears as activity without throughput.
Starvation appears as unfairness or work classes that rarely complete.
These symptoms matter in diagnostics.
7.4 Advanced concurrency is architecture
At this level the best solution is often not “add another lock”.
It is:
- change ownership
- change state shape
- change handoff mechanism
- reduce shared mutation
That is the main senior-level shift.
8. Interview Questions
1. What is safe publication?
It is making a fully initialized object visible to other threads through a valid publication edge.
2. Why is immutability powerful in concurrency?
Because it reduces or removes coordination after publication.
3. When is `ConcurrentHashMap` a good default?
When many threads share a map and the access pattern fits its concurrency model.
4. When is `CopyOnWriteArrayList` a good fit?
For read-mostly workloads such as listener registries.
5. Why is `BlockingQueue` often better than a custom lock protocol?
Because it encodes waiting, handoff, and pressure directly.
6. When is `LongAdder` better than `AtomicLong`?
Under high contention for counters.
7. What is the difference between deadlock and livelock?
Deadlock is stuck waiting.
Livelock is active but making no progress.
8. What is starvation?
A thread or class of work rarely gets needed resources because others dominate them.
9. Why do concurrent collections not solve every race condition?
Because compound business logic around them can still be non-atomic.
10. What is the senior-level takeaway?
The best concurrency fix is often a change in ownership and design, not a more complex lock.
9. Glossary
| Term | Meaning |
|---|---|
| thread safety | Correct behavior under concurrent use |
| immutability | State cannot change after construction |
| thread confinement | State is used by only one thread |
| safe publication | Correct visibility of a fully initialized object |
ConcurrentHashMap |
Concurrent shared map |
CopyOnWriteArrayList |
Read-optimized concurrent list |
BlockingQueue |
Blocking producer-consumer queue |
LongAdder |
Contention-friendly counter |
| deadlock | Cyclic waiting with no progress |
| starvation | Persistent unfair lack of resource access |
10. Cheatsheet
- Prefer less shared mutable state first
- Immutability and confinement are concurrency strategies too
- Safe publication is mandatory, not optional
ConcurrentHashMapis strong, but compound actions still matterCopyOnWriteArrayListis for read-mostly casesBlockingQueuegives handoff plus backpressure- Atomics help small state transitions, not every invariant
LongAdderhelps heavy-write counters- Distinguish deadlock, livelock, and starvation precisely
- Interjúban nevezd meg a safe publication és progress guarantee fogalmakat
🎮 Games
10 questions