Multithreading
Concurrency Best Practices
Write thread-safe code using immutable classes, thread-safe collections, and minimizing lock scope.
Interview: Commonly tested via design reviews: evaluate a block of code, identify concurrency bugs, and apply thread-safety optimizations.
Writing robust concurrent applications requires adopting defensive programming techniques. Immutable data, minimizing lock scopes, and preferring modern concurrency collections are the foundations of thread safety.
Core Idea
Thread safety is achieved by eliminating shared mutable state (immutability) or protecting it using concurrency utilities.
Why It Matters
Adhering to best practices prevents hard-to-reproduce, non-deterministic race conditions and visibility bugs.
Interview Lens
Expect code reviews where you must analyze thread-safety bugs and refactor code using ConcurrentHashMap or Immutable classes.
Key Concurrency Principles
- Immutability: Immutable objects (like Java
Recordclasses or final-field classes) have no mutable state. They are inherently thread-safe and can be shared freely without locks. - Concurrent Collections: Avoid wrapping legacy collections in
Collections.synchronizedMap()(which locks the entire collection). UseConcurrentHashMap(which locks on bucket segments, enabling parallel reads/writes) andCopyOnWriteArrayList(safe for read-heavy operations). - Minimize Lock Scope: Never hold a lock during expensive operations like network requests, database transactions, or file I/O to prevent system bottlenecks.
- Prefer JUC Utilities: Use
java.util.concurrenttools (like thread pools, locks, atomics) instead of low-levelwait()/notify().
Code Walkthrough
This class demonstrates implementing an immutable, thread-safe configuration holder.
public final class SystemConfig { private final String dbUrl; private final int maxConnections;public SystemConfig(String dbUrl, int maxConnections) { this.dbUrl = dbUrl; this.maxConnections = maxConnections; }
// No setters! Only getters return copy or primitive. public String getDbUrl() { return dbUrl; } public int getMaxConnections() { return maxConnections; } }
Interview-Relevant Information
Q: Why is ConcurrentHashMap more performant than a synchronized HashMap wrapper?
Answer: Collections.synchronizedMap locks the entire map on every read or write, forcing all threads to execute sequentially. ConcurrentHashMap uses segment-level locking (bucket-level lock stripping). Threads modifying different sections of the map can execute in parallel, and reads do not require locking.
Quick Checklist
How do you make a class immutable? Why should you avoid synchronized wrappers for high concurrency maps? If yes, you understand concurrency best practices.
Use Cases
Designing thread-safe singleton state engines in high-traffic APIs.
Safely managing concurrent user connection caches.
Common Mistakes
Holding locks during network/external service calls, which severely degrades system performance.
Performing non-atomic check-then-act sequences on ConcurrentHashMap (e.g. checking if a key exists before adding, which is a race condition; use putIfAbsent instead).