If you are preparing for a Java interview, immutability and concurrency are where “I know the syntax” turns into “I can explain why production systems stop lying under pressure.” Scala developers usually start with a healthy bias toward immutable data, which is great. The next step is learning where Java 21 gives you the same safety, where it makes you be more explicit, and how the concurrency toolbox fits together when several threads start poking the same state.
This guide organizes the existing posts in this repository into a progression that starts with boring, predictable data and ends with modern concurrency tools. It also includes a few planned follow-up posts that make sense after the current material, so you can see where the series is heading without needing placeholder articles.
Basic Level - Keep State Boring on Purpose
These are the foundation topics. Before you touch atomics, locks, or virtual threads, you should be comfortable explaining why immutable data reduces the number of things that can go wrong in the first place.
1. Immutable Data with Java Records
What It Is: Java records are the closest everyday Java gets to Scala case classes: concise data carriers with value-oriented semantics and far less boilerplate.
Read the full post: Immutable Data with Java Records
What You’ll Learn: You will see how records replace verbose “private final fields plus constructor plus equals/hashCode/toString” classes, how compact constructors enforce invariants, and where records map cleanly to Scala case classes and Kotlin data classes.
Interview Questions You Might Face:
- “Why are records a good fit for immutable domain data?”
- “How do records compare to Scala case classes?”
- “Can you validate data inside a record constructor?”
- “When would you still choose a normal class instead of a record?”
2. Immutability Beyond Records
What It Is: Records are a great start, but real immutability also means defensive copying, immutable collections, and making sure nobody can mutate your state through the side door.
Read the full post: Immutability Beyond Records
What You’ll Learn: This post shows how to build truly stable objects with List.copyOf,
Map.copyOf, validation rules, and replacement-style updates. It also makes the Java vs Scala vs Kotlin
mental model explicit, which is useful when interviewers ask why “read-only” is not always the same as
“immutable.”
Interview Questions You Might Face:
- “What makes an object truly immutable in Java 21?”
- “Why are defensive copies still important with records?”
- “How is Kotlin’s read-only collection model different from deep immutability?”
- “Why does immutability make concurrent code easier to reason about?”
Medium Level - Stop Threads from Gaslighting You
This is where concurrency stops being a vague “multiple things happen at once” story and becomes a set of specific guarantees. Visibility, atomicity, and concurrent collections solve different problems, and interviewers love asking about the boundary between them.
1. Visibility: The Case of the Disappearing Update
What It Is: Visibility is about whether one thread is guaranteed to observe another thread’s write, not whether the write happened at all.
Read the full post: Visibility: The Case of the Disappearing Update
What You’ll Learn: You will learn the practical meaning of the Java Memory Model, happens-before,
and volatile, along with the crucial rule that visibility is not the same thing as atomicity. This is
the post that explains why a stop flag can still look haunted without synchronization.
Interview Questions You Might Face:
- “What does
volatileactually guarantee?” - “What is happens-before in practical terms?”
- “Why can one thread fail to see another thread’s update?”
- “Why does
volatilenot fixcount++?”
2. Atomic Operations: Defuse the Race Condition
What It Is: Atomics are the tool you reach for when one value must be updated safely without a full lock-based critical section.
Read the full post: Atomic Operations: Defuse the Race Condition
What You’ll Learn: This post walks through AtomicInteger, AtomicReference, compare-and-set
loops, and LongAdder, using examples that show how two threads can both try to win the same race and
why one atomic variable is not the same thing as one atomic business transaction.
Interview Questions You Might Face:
- “What problem does
compareAndSetsolve?” - “When would you choose
AtomicReferenceoverAtomicInteger?” - “What is the difference between visibility and atomicity?”
- “When is
LongAddera better fit thanAtomicLong?”
3. Concurrent Collections: One Pot, Many Spoons
What It Is: Concurrent collections give multiple threads safe shared data structures, but they do not magically turn every multi-step workflow into a transaction.
Read the full post: Concurrent Collections: One Pot, Many Spoons.
What You’ll Learn: You will see where ConcurrentHashMap, BlockingQueue,
ConcurrentLinkedQueue, and CopyOnWriteArrayList shine, plus why map.get(k) followed by
map.put(k, v + 1) is still a trap even on a thread-safe map.
Interview Questions You Might Face:
- “What does a concurrent collection guarantee, and what does it not guarantee?”
- “Why is
mergesafer thangetplusputonConcurrentHashMap?” - “When would you choose
BlockingQueueoverConcurrentLinkedQueue?” - “Why can a thread-safe collection still be part of a broken business workflow?”
Advanced Level - Choose Your Concurrency Weapon Carefully
Once the basics are solid, the next interview questions are usually about architecture and trade-offs: virtual threads vs futures, locks vs atomics, message passing vs shared mutation, and when immutable snapshots are the cleanest escape hatch.
1. Virtual Threads and Structured Concurrency
What It Is: Project Loom makes the “one thread per task” model practical again by giving Java lightweight virtual threads and structured coordination tools.
Read the full post: Virtual Threads and Structured Concurrency
What You’ll Learn: This post covers migration from thread pools to virtual threads,
StructuredTaskScope, scoped values, and the important caveat of virtual-thread pinning. It is the
best starting point for explaining modern Java concurrency beyond the classic executor story.
Interview Questions You Might Face:
- “What makes virtual threads different from platform threads?”
- “What is structured concurrency trying to improve?”
- “What is thread pinning, and why should you care?”
- “When are virtual threads a great fit, and when are they not?”
2. CompletableFuture and Asynchronous Programming
What It Is: CompletableFuture is Java’s established async composition API for chaining,
combining, and recovering from asynchronous work without blocking the caller immediately.
Read the full post: CompletableFuture and Asynchronous Programming
What You’ll Learn: You will revisit thenApply, thenCompose, allOf, timeouts, and failure
recovery, and you will also get the important interview comparison with Scala Future and Kotlin
coroutines.
Interview Questions You Might Face:
- “What is the difference between
thenApplyandthenCompose?” - “How do you combine several asynchronous calls in Java?”
- “How do you recover from failures in a
CompletableFuturepipeline?” - “When would you choose virtual threads over
CompletableFuture?”
3. ZIO Fibres vs Java Virtual Threads vs Kotlin Coroutines
What It Is: This is the cross-language comparison post for people who want to explain the same concurrency idea in Java, Scala, and Kotlin without pretending the trade-offs are identical.
Read the full post: ZIO Fibres vs Java Virtual Threads vs Kotlin Coroutines
What You’ll Learn: The post compares lightweight concurrency models, error handling, cancellation, and structured composition across the three ecosystems. It is especially useful for Scala developers who want a precise answer to “What feels familiar in Java 21, and what still works differently?”
Interview Questions You Might Face:
- “How do virtual threads compare to coroutines or fibres?”
- “What do you gain from Scala effect systems that Java does not track in the type system?”
- “Why might a polyglot team choose different concurrency tools in different modules?”
- “What is the simplest migration path for an existing Java service?”
4. Locks: When One Atomic Value Isn’t Enough
What It Is: Sometimes one field is not the problem. The real problem is that several pieces of state must change together, and atomics alone cannot protect the whole dance.
Read the full post: Locks: When One Atomic Value Isn’t Enough
What You’ll Learn: This post covers synchronized vs ReentrantLock, cross-field invariants,
lock scope, deadlock avoidance, and how virtual-thread pinning changes the conversation in Java 21.
Interview Questions You Might Face:
- “When is a lock better than an atomic variable?”
- “What trade-offs exist between
synchronizedandReentrantLock?” - “How do you protect several related fields consistently?”
- “What is the relationship between locks and virtual-thread pinning?”
5. Passing Messages Instead of Sharing State
What It Is: One of the best concurrency tricks is refusing to play the shared-mutable-state game at all. Instead of many threads editing the same object, they hand work to each other through queues and clear ownership boundaries.
Read the full post: Coming soon - planned follow-up post.
What You’ll Learn: This planned article should connect producer-consumer queues, mailbox-style processing, blocking vs non-blocking handoff, and why message passing often feels more natural to Scala developers who already think in terms of isolation and explicit effects.
Interview Questions You Might Face:
- “When is message passing simpler than shared-state coordination?”
- “What is the difference between a queue-based design and a lock-heavy design?”
- “How do ownership boundaries reduce race conditions?”
- “What Java 21 building blocks help with message-passing architectures?”
6. Immutable Snapshots of Changing State
What It Is: Some systems do change over time, but readers still want a consistent picture. This is where immutable snapshots become the grown-up answer to “please stop mutating things while I’m looking at them.”
Read the full post: Coming soon - planned follow-up post.
What You’ll Learn: This planned article should cover publishing new immutable snapshots with
AtomicReference, copy-on-write style patterns, stable reads, and when whole-state replacement is
cleaner than fine-grained locking.
Interview Questions You Might Face:
- “Why would you publish a new immutable snapshot instead of mutating in place?”
- “How does
AtomicReferencepair with immutable state?” - “When is copy-on-write a smart trade-off, and when is it too expensive?”
- “How does this pattern map to Scala’s value-oriented style?”
How to Use This Guide
If you are new to this area, take the posts in order. The sequence is deliberate:
- Learn to model stable state with records and immutable collections.
- Learn why visibility and atomicity are different promises.
- Learn which concurrent collections solve structure-level sharing problems.
- Learn when higher-level concurrency styles such as virtual threads, futures, locks, and message passing make sense.
The big interview takeaway is this: immutability is not a separate topic from concurrency. It is often the first concurrency optimization because the safest shared state is the state nobody can mutate.
Conclusion
For Scala developers moving into Java 21, this series is really about translating instincts into the Java toolbox. Start with immutable data, add visibility rules, reach for atomics and concurrent collections when mutation becomes unavoidable, and only then move to bigger coordination tools such as locks, message passing, futures, or virtual threads.
That path gives you better interview answers and better production instincts, which is a lovely two-for-one deal.