Implementing Concurrency Control

1. Designing Thread-Safe Services

PatternDetail
StatelessEasiest; default for Spring beans
Immutable stateSafe to share
Confined stateThreadLocal
SynchronizedGuarded mutation
Concurrent collectionsConcurrentHashMap

2. Implementing Synchronization Mechanisms

ToolUse
synchronizedSimple monitor lock
ReentrantLockTry-lock, timeouts, fairness
ReadWriteLockMany readers / 1 writer
StampedLockOptimistic reads
volatileVisibility, no atomicity
AtomicInteger/ReferenceLock-free counters

3. Using Concurrent Data Structures

StructureUse
ConcurrentHashMapGeneral concurrent map
CopyOnWriteArrayListRead-heavy, write-rare
BlockingQueueProducer/consumer
ConcurrentLinkedQueueLock-free FIFO
ConcurrentSkipListMapSorted 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

ConcernDetail
Concurrent retriesUse unique key + insert
DB constraintUNIQUE on idempotency_key
Catch dupReturn existing result

6. Handling Race Conditions

TypeFix
Check-then-actAtomic compare-and-set
Read-modify-writeOptimistic lock or single SQL
Lost update@Version / SELECT FOR UPDATE
Double executionIdempotency / distributed lock

7. Designing Deadlock Prevention

StrategyDetail
Lock orderingAlways acquire in same order (e.g., by ID)
TimeoutstryLock(timeout)
Lock-freeUse atomics, immutables
Coarser locksSometimes 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

WhyDetail
Non-blockingFewer threads needed
BackpressureProducer respects consumer rate
ComposableOperators chain
CostSteeper 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

ToolDetail
AtomicReferenceCAS-based updates
LongAdderHigh-throughput counter
ConcurrentHashMap.compute*Atomic compound operations
DisruptorLock-free ring buffer
Note: Java 21 virtual threads NEW let you write blocking code at scale — often a simpler alternative to reactive.