线程与 Executor
直接 new Thread 难以管理、开销大。用 ExecutorService 线程池复用线程,统一提交任务。
ExecutorService pool = Executors.newFixedThreadPool(8);
Future<String> f = pool.submit(() -> doWork());
String r = f.get(); // 阻塞取结果
pool.shutdown();Executors.newCachedThreadPool 可能无限创建线程。生产用 ThreadPoolExecutor 显式设定核心/最大线程与有界队列。
线程池调优
CPU 密集任务线程数≈核数;I/O 密集可适当放大。队列用于缓冲,但过大会增加延迟、掩盖背压问题。
new ThreadPoolExecutor(
8, 16,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1024),
new ThreadPoolExecutor.CallerRunsPolicy()); // 满时由调用者线程执行,形成背压并发集合
普通 HashMap/ArrayList 非线程安全。并发场景用 ConcurrentHashMap、CopyOnWriteArrayList、BlockingQueue 等。
ConcurrentHashMap<String, AtomicInteger> counts = new ConcurrentHashMap<>();
counts.computeIfAbsent("a", k -> new AtomicInteger()).incrementAndGet();读多写少用 CopyOnWriteArrayList;生产者消费者用 BlockingQueue 天然解耦。
锁机制
synchronized 简单够用;ReentrantLock 支持超时、可中断、公平性与多条件变量,更灵活。
private final ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// 临界区
} finally {
lock.unlock(); // 必须 finally 释放
}临界区只包真正共享可变状态,锁内别做 I/O 或远程调用,否则严重降低并发。
原子变量
AtomicInteger/Long/Reference 用 CAS 实现无锁原子操作,适合计数器、状态标记等,比锁更轻。
private final AtomicLong seq = new AtomicLong();
long id = seq.incrementAndGet(); // 线程安全自增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);虚拟线程
Java 21 的虚拟线程(Project Loom)由 JVM 调度,成千上万轻量线程运行在少量平台线程上,阻塞成本低,编写同步风格即得高并发。
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 10_000; i++) {
executor.submit(() -> doIoBoundWork()); // 每个任务一个虚拟线程
}
}虚拟线程廉价到"每个任务一个"即可,不要再套固定大小线程池;避免在虚拟线程里用 synchronized 长时间持锁(会固定载体线程)。
常见陷阱与 FAQ
出现竞态:丢失更新、读到中间态。要么用锁/原子,要么用不可变对象 + 线程封闭(每个线程独有)。
被吞掉,除非用 Future.get() 或设置 ThreadFactory 的未捕获处理器。务必处理异常,否则问题静默。
对 I/O 密集型收益巨大;CPU 密集型仍受核数限制。它简化的是"大量阻塞任务"的写法,不消除并行计算的物理上限。