Java并发编程经历了从低级同步原语到高级并发工具,再到革命性虚拟线程的漫长演进。每个阶段都在解决前一阶段的痛点,同时引入新的抽象层次。理解这条演进路线,对于编写正确、高性能的并发程序至关重要。
参考:https://qfcrz.cn/category/smart-control.html
Java1.0-1.4的原始时代:早期的并发支持只有synchronized关键字、Object.wait/notify、以及Thread类。synchronized提供互斥,但粗粒度锁导致性能低下。wait/notify容易出错(错过通知、虚假唤醒)。Thread直接映射到操作系统线程,创建和销毁成本高昂。
这个时代的典型模式是线程池(自己实现)和生产者-消费者队列。ThreadLocal提供了线程局部变量,但容易内存泄漏(忘记remove)。
Java5的java.util.concurrent革命:Java5引入了java.util.concurrent包,这是并发编程的里程碑。ExecutorService和ThreadPoolExecutor标准化了线程池管理。Callable和Future支持有返回值的任务。BlockingQueue简化了生产者-消费者模式。ConcurrentHashMap提供高并发哈希表。CountDownLatch、CyclicBarrier、Semaphore等同步器覆盖了常见场景。AtomicXXX类提供了无锁的原子操作。
Java5的并发工具使开发者无需重复造轮子,大幅降低了并发编程的门槛。
Java7的Fork/Join框架:Java7引入了ForkJoinPool,专门用于分治任务(如递归算法、并行计算)。RecursiveTask和RecursiveAction实现分治逻辑,ForkJoinPool使用工作窃取算法——空闲线程从其他线程的队列尾部窃取任务,保持CPU忙碌。Fork/Join是Java8并行流的基础。
Java8的并行流和CompletableFuture:并行流(parallelStream)将集合操作自动并行化,开发者无需手动管理线程。CompletableFuture是Future的增强,支持回调链、组合操作、异常处理。thenApply、thenCompose、thenCombine等方法使异步编程更加声明式。CompletableFuture填补了Java在异步编程方面的空白,与反应式编程(RxJava、ProjectReactor)形成竞争。
参考:https://aescc.cn/category/balcony.html
Java9-11的细微改进:Java9引入了CompletableFuture的超时和延迟执行方法(orTimeout、completeOnTimeout)。Flow类实现了响应式流规范(ReactiveStreams),但使用较少。Java11主要稳定现有功能,没有重大并发特性。
Java19-21的虚拟线程革命:ProjectLoom引入的虚拟线程是Java并发最大的变革。虚拟线程是JVM管理的轻量级线程,不直接映射到操作系统线程。数百万个虚拟线程可以在少量操作系统线程上运行。当虚拟线程执行阻塞操作时,它会从载体线程上卸载,载体线程切换到其他虚拟线程,阻塞不占用操作系统线程资源。
虚拟线程彻底改变了Java的并发编程模型。开发者可以回到“一个请求一个线程”的简单模型,在需要I/O时直接阻塞,无需复杂的异步回调。虚拟线程的创建和切换成本比平台线程低数千倍,使百万并发成为现实。
虚拟线程的API与传统线程几乎相同:Thread.startVirtualThread或Executors.newVirtualThreadPerTaskExecutor()。现有代码通常只需替换Executors.newCachedThreadPool为虚拟线程执行器即可获得性能提升。
虚拟线程不是银弹。它最适合I/O密集型应用,对于CPU密集型任务,平台线程仍然更合适。虚拟线程中的synchronized会导致载体线程被固定(pinned),应尽可能使用ReentrantLock替代。ThreadLocal在虚拟线程中需要谨慎使用,因为大量虚拟线程可能耗尽内存。
参考:https://wkmsa.cn/category/sleep-environment.html
结构化并发(Java21预览)是虚拟线程的配套特性。StructuredTaskScope将并发任务组织为树状结构,子任务的生命周期不会超出父任务的范围。结构化并发自动处理取消和错误传播,防止线程泄漏。它是并发编程的范式转变,使代码更易推理和调试。
作用域值(Java20孵化,Java21预览)是ThreadLocal的替代方案。作用域值是不可变的,只能在限定的作用域内设置,在线程间传递。与ThreadLocal不同,作用域值不会在虚拟线程数量巨大时导致内存泄漏,且传递效率更高。
并发编程的最佳实践演进:从直接使用Thread到使用ExecutorService,再到使用CompletableFuture,再到虚拟线程,抽象层次不断提高。不变的是:避免共享可变状态,优先使用不可变对象,使用高级并发工具而非原始同步,以及充分测试并发代码(JCStress等工具)。
Java并发编程的未来是虚拟线程主导的。它将简化并发代码,提高I/O密集型应用的吞吐量,同时保持与现有生态的兼容性。开发者应该开始学习虚拟线程,准备在Java21+环境中采用。
参考:https://qfcrz.cn