Implementing Concurrency Control
1. Designing Thread-Safe Services
| Pattern | Detail |
|---|---|
| Stateless | Easiest; default for Spring beans |
| Immutable state | Safe to share |
| Confined state | ThreadLocal |
| Synchronized | Guarded mutation |
| Concurrent collections | ConcurrentHashMap |
2. Implementing Synchronization Mechanisms
| Tool | Use |
|---|---|
synchronized | Simple monitor lock |
ReentrantLock | Try-lock, timeouts, fairness |
ReadWriteLock | Many readers / 1 writer |
StampedLock | Optimistic reads |
volatile | Visibility, no atomicity |
AtomicInteger/Reference | Lock-free counters |
3. Using Concurrent Data Structures
| Structure | Use |
|---|---|
ConcurrentHashMap | General concurrent map |
CopyOnWriteArrayList | Read-heavy, write-rare |
BlockingQueue | Producer/consumer |
ConcurrentLinkedQueue | Lock-free FIFO |
ConcurrentSkipListMap | Sorted concurrent map |
4. Implementing Async Processing
Example: Spring @Async
@Configuration @EnableAsync
public class AsyncConfig {}
@Service
public class EmailService {
@Async("emailExecutor")
public CompletableFuture<Void> sendWelcome(User u) {
client.send(u);
return CompletableFuture.completedFuture(null);
}
}
5. Implementing Idempotency Keys
| Concern | Detail |
|---|---|
| Concurrent retries | Use unique key + insert |
| DB constraint | UNIQUE on idempotency_key |
| Catch dup | Return existing result |
6. Handling Race Conditions
| Type | Fix |
|---|---|
| Check-then-act | Atomic compare-and-set |
| Read-modify-write | Optimistic lock or single SQL |
| Lost update | @Version / SELECT FOR UPDATE |
| Double execution | Idempotency / distributed lock |
7. Designing Deadlock Prevention
| Strategy | Detail |
|---|---|
| Lock ordering | Always acquire in same order (e.g., by ID) |
| Timeouts | tryLock(timeout) |
| Lock-free | Use atomics, immutables |
| Coarser locks | Sometimes simpler than fine-grained |
8. Implementing Thread Pools
Example: Bounded executor
ExecutorService exec = new ThreadPoolExecutor(
/* core */ 8, /* max */ 32,
60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1000),
new ThreadFactoryBuilder().setNameFormat("worker-%d").build(),
new ThreadPoolExecutor.CallerRunsPolicy()); // backpressure
Warning: Avoid
Executors.newCachedThreadPool() in production — unbounded threads can OOM.9. Using Reactive Programming
| Why | Detail |
|---|---|
| Non-blocking | Fewer threads needed |
| Backpressure | Producer respects consumer rate |
| Composable | Operators chain |
| Cost | Steeper learning curve, harder debugging |
10. Implementing Read-Write Locks
Example: ReentrantReadWriteLock
private final ReadWriteLock rw = new ReentrantReadWriteLock();
public V get(K k) { rw.readLock().lock(); try { return map.get(k); } finally { rw.readLock().unlock(); } }
public void put(K k, V v) { rw.writeLock().lock(); try { map.put(k, v); } finally { rw.writeLock().unlock(); } }
11. Implementing Semaphores
Example: Concurrency limit
private final Semaphore inflight = new Semaphore(50);
public Result call() throws InterruptedException {
inflight.acquire();
try { return downstream.invoke(); }
finally { inflight.release(); }
}
12. Implementing Lock-Free Algorithms
| Tool | Detail |
|---|---|
AtomicReference | CAS-based updates |
LongAdder | High-throughput counter |
ConcurrentHashMap.compute* | Atomic compound operations |
| Disruptor | Lock-free ring buffer |
Note: Java 21 virtual threads NEW let you write blocking code at scale — often a simpler alternative to reactive.