Java 后端手册JAVA BACKEND

CHAPTER 08生产层 · CONCURRENCY

并发模型

Thread、Executor、并发集合、锁、CompletableFuture 与虚拟线程。写出正确且高效的高并发后端。

开始阅读难度高阶先修第二、七章约3小时
08.1

线程与 Executor

直接 new Thread 难以管理、开销大。用 ExecutorService 线程池复用线程,统一提交任务。

ExecutorService pool = Executors.newFixedThreadPool(8);
Future<String> f = pool.submit(() -> doWork());
String r = f.get();            // 阻塞取结果
pool.shutdown();
别用无界队列

Executors.newCachedThreadPool 可能无限创建线程。生产用 ThreadPoolExecutor 显式设定核心/最大线程与有界队列。

08.2

线程池调优

CPU 密集任务线程数≈核数;I/O 密集可适当放大。队列用于缓冲,但过大会增加延迟、掩盖背压问题。

new ThreadPoolExecutor(
    8, 16,
    60L, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(1024),
    new ThreadPoolExecutor.CallerRunsPolicy()); // 满时由调用者线程执行,形成背压
08.3

并发集合

普通 HashMap/ArrayList 非线程安全。并发场景用 ConcurrentHashMap、CopyOnWriteArrayList、BlockingQueue 等。

ConcurrentHashMap<String, AtomicInteger> counts = new ConcurrentHashMap<>();
counts.computeIfAbsent("a", k -> new AtomicInteger()).incrementAndGet();
false sharing 之外

读多写少用 CopyOnWriteArrayList;生产者消费者用 BlockingQueue 天然解耦。

08.4

锁机制

synchronized 简单够用;ReentrantLock 支持超时、可中断、公平性与多条件变量,更灵活。

private final ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
    // 临界区
} finally {
    lock.unlock();           // 必须 finally 释放
}
锁范围要小

临界区只包真正共享可变状态,锁内别做 I/O 或远程调用,否则严重降低并发。

08.5

原子变量

AtomicInteger/Long/Reference 用 CAS 实现无锁原子操作,适合计数器、状态标记等,比锁更轻。

private final AtomicLong seq = new AtomicLong();
long id = seq.incrementAndGet();   // 线程安全自增
08.6

CompletableFuture

异步编排利器:thenApply/thenCompose/thenCombine 链式处理,allOf 并行汇聚,把回调地狱变成可读流水线。

CompletableFuture<User> u = getUserAsync(id);
CompletableFuture<List<Order>> o = getOrdersAsync(id);
u.thenCombine(o, (user, orders) -> new Profile(user, orders))
 .thenAccept(System.out::println);
08.7

虚拟线程

Java 21 的虚拟线程(Project Loom)由 JVM 调度,成千上万轻量线程运行在少量平台线程上,阻塞成本低,编写同步风格即得高并发。

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 10_000; i++) {
        executor.submit(() -> doIoBoundWork());   // 每个任务一个虚拟线程
    }
}
虚拟线程与池

虚拟线程廉价到"每个任务一个"即可,不要再套固定大小线程池;避免在虚拟线程里用 synchronized 长时间持锁(会固定载体线程)。

08.8

常见陷阱与 FAQ

共享可变状态不加锁会怎样?

出现竞态:丢失更新、读到中间态。要么用锁/原子,要么用不可变对象 + 线程封闭(每个线程独有)。

线程池任务抛异常会怎样?

被吞掉,除非用 Future.get() 或设置 ThreadFactory 的未捕获处理器。务必处理异常,否则问题静默。

虚拟线程能替代所有并发写法吗?

对 I/O 密集型收益巨大;CPU 密集型仍受核数限制。它简化的是"大量阻塞任务"的写法,不消除并行计算的物理上限。

08.9

实战练习与资源

延伸资源