---
description: Java Concurrency
alwaysApply: false
---

# Java Concurrency

The Java Memory Model is the law. Every concurrent decision must respect it.

## Virtual Threads (Java 21+)

Default for I/O-bound work. Platform threads for CPU-bound.

```java
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    var futures = tasks.stream()
        .map(task -> executor.submit(() -> process(task)))
        .toList();
    // Collect results with proper timeout/exception handling
}
```

- Don't pool virtual threads — they're cheap to create
- Use `ReentrantLock` over `synchronized` (synchronized pins to carrier thread)
- Don't use `ThreadLocal` — use `ScopedValue` (preview)

## Structured Concurrency (Preview)

```java
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    var userTask = scope.fork(() -> userService.findById(userId));
    var ordersTask = scope.fork(() -> orderService.findByUser(userId));
    scope.join().throwIfFailed();
    return new Dashboard(userTask.get(), ordersTask.get());
}
```

## CompletableFuture

```java
var userFuture = CompletableFuture.supplyAsync(() -> userService.findById(userId));
var ordersFuture = CompletableFuture.supplyAsync(() -> orderService.recent(userId));
CompletableFuture.allOf(userFuture, ordersFuture)
    .thenApply(v -> new Dashboard(userFuture.join(), ordersFuture.join()));
```

## Thread Safety Patterns

- **Immutability** (preferred): records, `List.of()`, `final` fields
- **Atomics**: `AtomicLong`, `AtomicReference` for counters and config refs
- **ConcurrentHashMap**: use `computeIfAbsent`/`merge` — never iterate and modify
- **Bounded queues**: `LinkedBlockingQueue(capacity)` for backpressure

## Interrupt Handling

```java
// Always restore interrupt status
try { Thread.sleep(Duration.ofSeconds(5)); }
catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    throw new RuntimeException("Interrupted", e);
}
```

## Anti-Patterns

- **Never** `synchronized` on non-final fields — threads may lock different objects
- **Never** `Thread.stop()`/`suspend()` — use cooperative interruption
- **Never** busy-wait `while (!ready) {}` — use CountDownLatch, Semaphore, or BlockingQueue
