Java Concurrency: Threads, Locks, Thread Pools & CompletableFuture
Creating threads, synchronized and volatile, ReentrantLock, ThreadPoolExecutor parameters, and composing async work with CompletableFuture — the concurrency knowledge backend interviews demand.
Three Ways to Create a Thread
// 1. 继承 Thread
Thread t1 = new Thread() {
public void run() { System.out.println("t1"); }
};
t1.start();
// 2. 实现 Runnable(推荐)
Thread t2 = new Thread(() -> System.out.println("t2"));
t2.start();
// 3. Callable + FutureTask:带返回值
FutureTask<Integer> ft = new FutureTask<>(() -> 42);
new Thread(ft).start();
System.out.println(ft.get()); // 阻塞等结果
start() creates the OS thread and calls run(); calling run() directly just runs synchronously on the current thread. Never start the same Thread twice.
synchronized and volatile
synchronized provides both mutual exclusion and visibility; volatile only guarantees visibility (no interleaving of reads/writes) but not atomicity of compound operations like i++.
synchronized void inc() { count++; } // 锁 this
static synchronized void inc2() { c2++; } // 锁 Class 对象
Object lock = new Object();
synchronized (lock) { /* critical section */ }
// volatile:轻量可见性
private volatile boolean running = true;
public void stop() { running = false; } // 其他线程立即可见
// 原子类(CAS,无锁)
AtomicInteger counter = new AtomicInteger();
counter.incrementAndGet();
counter.compareAndSet(0, 1);
ReentrantLock vs synchronized
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// critical section
} finally {
lock.unlock(); // 必须在 finally 释放
}
// 公平锁、可中断、tryLock 超时是 synchronized 没有的能力
lock.tryLock(3, TimeUnit.SECONDS);
- synchronized: simpler, JVM-optimized (biased locking removed in 15+, sync eras), cannot time out.
- ReentrantLock: fair ordering, lockInterruptibly, tryLock with timeout, multiple Condition queues.
Thread Pools: ThreadPoolExecutor
Never use newFixedThreadPool / newCachedThreadPool blindly in production — their unbounded queues or unbounded threads can exhaust memory. Understand the 7 parameters of ThreadPoolExecutor.
ThreadPoolExecutor pool = new ThreadPoolExecutor(
4, // corePoolSize 核心线程数
16, // maximumPoolSize 最大线程数
60, TimeUnit.SECONDS, // keepAliveTime 空闲回收时间
new LinkedBlockingQueue<>(200), // workQueue 任务队列(有界!)
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
pool.submit(() -> doWork());
pool.shutdown();
- Task arrives: if threads < corePoolSize, create a core thread.
- Otherwise it is queued; if the queue is full and threads < max, create a non-core thread.
- If both are exhausted, the rejection policy runs: Abort throws, CallerRuns runs on the caller, Discard drops.
Size rule of thumb: CPU-bound tasks -> N_cores + 1 threads; IO-bound (DB, HTTP) -> 2 * N_cores or estimate with (target QPS * avg latency in s).
CompletableFuture for Async Composition
Executor io = Executors.newFixedThreadPool(16);
CompletableFuture<User> userFuture =
CompletableFuture.supplyAsync(() -> userService.findById(1L), io);
CompletableFuture<Order> orderFuture =
CompletableFuture.supplyAsync(() -> orderService.findLatest(1L), io);
// 两个异步任务都完成后合并结果
userFuture.thenCombineAsync(orderFuture, (u, o) -> {
Profile p = new Profile();
p.setUser(u); p.setOrder(o);
return p;
}, io).thenAccept(System.out::println);
// 超时与异常
orderFuture.orTimeout(2, TimeUnit.SECONDS)
.exceptionally(ex -> Order.empty());创建线程的三种方式
// 1. 继承 Thread
Thread t1 = new Thread() {
public void run() { System.out.println("t1"); }
};
t1.start();
// 2. 实现 Runnable(推荐)
Thread t2 = new Thread(() -> System.out.println("t2"));
t2.start();
// 3. Callable + FutureTask:带返回值
FutureTask<Integer> ft = new FutureTask<>(() -> 42);
new Thread(ft).start();
System.out.println(ft.get()); // 阻塞等结果
start() 会创建操作系统线程并调用 run();直接调用 run() 只是在当前线程同步执行。同一个 Thread 对象不能 start 两次。
synchronized 与 volatile
synchronized 同时保证互斥与可见性;volatile 只保证可见性(读写不与其它操作重排),不保证 i++ 这类复合操作的原子性。
synchronized void inc() { count++; } // 锁 this
static synchronized void inc2() { c2++; } // 锁 Class 对象
Object lock = new Object();
synchronized (lock) { /* critical section */ }
// volatile:轻量可见性
private volatile boolean running = true;
public void stop() { running = false; } // 其他线程立即可见
// 原子类(CAS,无锁)
AtomicInteger counter = new AtomicInteger();
counter.incrementAndGet();
counter.compareAndSet(0, 1);
ReentrantLock 与 synchronized 对比
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// critical section
} finally {
lock.unlock(); // 必须在 finally 释放
}
// 公平锁、可中断、tryLock 超时是 synchronized 没有的能力
lock.tryLock(3, TimeUnit.SECONDS);
- synchronized:写法简单、JVM 深度优化(15+ 移除偏向锁),不支持超时。
- ReentrantLock:支持公平锁、可中断、tryLock 超时、多个 Condition 等待队列。
线程池:ThreadPoolExecutor
生产环境不要图省事直接用 newFixedThreadPool / newCachedThreadPool——它们的无界队列或无限线程可能耗尽内存。关键是理解 ThreadPoolExecutor 的 7 个参数。
ThreadPoolExecutor pool = new ThreadPoolExecutor(
4, // corePoolSize 核心线程数
16, // maximumPoolSize 最大线程数
60, TimeUnit.SECONDS, // keepAliveTime 空闲回收时间
new LinkedBlockingQueue<>(200), // workQueue 任务队列(有界!)
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
pool.submit(() -> doWork());
pool.shutdown();
- 任务到来:线程数小于核心线程数时创建核心线程。
- 否则进队列排队;队列满且线程数小于最大值时,创建非核心线程。
- 都满了执行拒绝策略:Abort 抛异常、CallerRuns 由提交线程自己执行、Discard 丢弃。
线程数经验值:CPU 密集型任务用 核心数+1;IO 密集型(数据库、HTTP 调用)用 2×核心数,或按 目标 QPS × 平均耗时(秒) 估算。
CompletableFuture 异步组合
Executor io = Executors.newFixedThreadPool(16);
CompletableFuture<User> userFuture =
CompletableFuture.supplyAsync(() -> userService.findById(1L), io);
CompletableFuture<Order> orderFuture =
CompletableFuture.supplyAsync(() -> orderService.findLatest(1L), io);
// 两个异步任务都完成后合并结果
userFuture.thenCombineAsync(orderFuture, (u, o) -> {
Profile p = new Profile();
p.setUser(u); p.setOrder(o);
return p;
}, io).thenAccept(System.out::println);
// 超时与异常
orderFuture.orTimeout(2, TimeUnit.SECONDS)
.exceptionally(ex -> Order.empty());