从互斥锁到无锁,Java 20年并发安全进化史
Java自1996年诞生以来其并发安全方面演进史就是一部从悲观互斥向乐观并行持续进化的历史。本文将沿着JDK版本发布的时间线梳理Java在解决线程安全问题时在锁机制、同步工具以及无锁算法上的关键变革。第一章混沌初开——JDK 1.0的原始线程模型1996年1996年1月JDK 1.0的发布奠定了Java最基础的线程模型。这个版本虽然功能原始但确立了一个具有传承性的设计——Java采用了协作式的线程通信方式。1.1 基础原语synchronized与wait/notify从第一个版本开始Java就引入了synchronized关键字作为内置锁Intrinsic Lock以及与之配套的wait()/notify()方法进行线程间通信。其设计核心非常简洁非静态方法使用synchronized修饰相当于锁住当前实例对象synchronized(this)。静态方法使用synchronized修饰相当于锁住该类的Class对象synchronized(XXX.class)。这是Java并发安全的起点——悲观锁思想的体现线程进入同步块前必须获得锁其他线程只能阻塞等待。在当时的技术背景下这种设计简单直接足以应对基础的多线程需求。1.2 早期设计的遗憾被废弃的暴力方法JDK 1.0初期还提供了stop()、suspend()和resume()三个方法。但由于它们本质上是不安全的——stop()直接终止线程会导致对象状态被破坏suspend()挂起线程时持有锁极易引发死锁——这些方法在JDK 1.2中就被正式废弃了。这一事件给Java开发者上了一课并发控制不能靠暴力中断而需要线程间的自觉配合。第二章理论奠基——JDK 1.2到1.4的修修补补1998-2002年在Java诞生的前六年开发者们饱受并发问题的困扰却往往归咎于代码逻辑而忽视了更深层的原因Java内存模型JMM的定义存在缺陷。2.1 可见性难题早期的JMM缺乏严谨的先行发生happens-before规则。一个线程修改了共享变量另一个线程可能永远看不到这个修改这就是可见性问题。当时volatile关键字虽然被设计用来解决这个问题但在旧的JMM下volatile的语义并不足够强不能完全解决这个问题。2.2 民间力量的酝酿Doug Lea的贡献在这一时期并发编程领域的泰斗Doug Lea开始撰写一系列并发工具类包括后来成为JUC核心的ReentrantLock、ConcurrentHashMap等。这些代码虽然没有被纳入JDK但其设计思想和实现方案为后来的标准化奠定了基础。这个时期的Java处于一个尴尬的境地硬件已经进入多核时代但语言层面的并发模型还停留在单核时代。第三章革命前夜——JDK 1.5的里程碑式突破2004年2004年JDK 1.5即Java 5的发布堪称Java并发编程史上最重要的里程碑。这一年JSR 133规范为Java带来了全新的内存模型JSR 166引入了划时代的java.util.concurrent包。3.1 JSR 133的救赎内存模型的重新定义JSR 133重新定义了volatile和synchronized的语义明确了happens-before关系对一个volatile变量的写操作happens-before于后续对这个变量的读操作。这意味着只要写入volatile变量就相当于向内存发出刷新信号读取volatile变量则相当于从内存中加载最新值。这一修订为后来高性能并发工具的发展扫清了理论障碍。3.2 JSR 166的降临JUC包的诞生由Doug Lea主导的JSR 166专家组正式将一系列并发工具纳入JDK标准库包括显式锁ReentrantLock、ReadWriteLock同步器Semaphore、CountDownLatch、CyclicBarrier并发容器ConcurrentHashMap、CopyOnWriteArrayList线程池ThreadPoolExecutor、Executors3.3 CAS的正式登场原子变量类的革命JDK 1.5最具有前瞻性的设计是引入了java.util.concurrent.atomic包提供了AtomicInteger、AtomicLong、AtomicReference等一系列原子类。这些类的底层实现正是CASCompare-And-Swap比较并交换——一种源自硬件层面的乐观并发控制技术。3.3.1 硬件级的乐观锁CAS是一种乐观锁策略。它包含三个操作数内存位置V、期望值A、新值B。只有当V的值等于A时才将V更新为B且整个操作是原子的。CAS依赖于现代CPU提供的特定指令如x86架构的cmpxchg由硬件保证其原子性避免了用户态锁的介入。它的核心思想是假设没有冲突能改就改如果改的时候发现被动了那就重试。3.3.2 无锁编程的实践以AtomicInteger为例其incrementAndGet()方法的内部实现是一个典型的CAS循环publicfinalintincrementAndGet(){for(;;){intcurrentget();intnextcurrent1;if(compareAndSet(current,next))returnnext;}}这种循环重试的模式就是无锁编程的雏形。虽然它会占用CPU循环但在低冲突场景下远比挂起线程高效。这正是乐观锁思想的精髓用CPU周期换取线程的存活用自旋替代阻塞。3.3.3 ReentrantLock的CAS内核值得注意的是即便是ReentrantLock这样的显式锁其底层实现也大量依赖CAS。AbstractQueuedSynchronizerAQS作为JUC锁的基石其核心状态state就是一个被CAS操作的volatile变量。获取锁、释放锁的成败都取决于能否通过CAS成功修改这个状态。可以说CAS是JUC框架的发动机。3.4 UnsafeCAS的底层支撑JDK 1.5中CAS的广泛应用离不开一个核心底层工具类——sun.misc.Unsafe。该类提供了直接操作内存、执行原子操作的底层能力是Java并发工具如原子类、AQS实现的核心依赖也是CAS操作能够落地的关键。Unsafe的设计初衷是为JDK内部类如java.util.concurrent.atomic、java.util.concurrent.locks提供底层支持并非面向普通开发者。它的核心能力包括原子操作提供compareAndSwapInt、compareAndSwapLong、compareAndSwapObject等方法直接封装CPU的CAS指令是AtomicInteger等原子类的底层实现。例如AtomicInteger的compareAndSet方法本质就是调用Unsafe的compareAndSwapInt方法。内存操作可直接分配、释放内存操作对象的字段无需通过反射突破了Java的安全限制能直接操作堆外内存为高性能并发提供了可能。线程调度提供park和unpark方法用于线程的挂起和唤醒是AQS实现线程等待队列的核心底层方法。需要注意的是Unsafe具有极高的危险性它绕过了Java的安全检查直接操作内存和底层资源一旦使用不当极易引发内存泄漏、数据错乱甚至JVM崩溃。因此JDK官方一直未将其公开暴露普通开发者无法直接通过正常方式获取其实例需通过反射且该类在后续JDK版本中逐渐被更安全的API替代。3.5 JDK 1.5的历史定位JDK 1.5标志着Java并发编程从单一路径走向多元化并发模型代表技术适用场景悲观阻塞synchronized简单同步低并发显式锁ReentrantLock需要超时、中断等高级功能无锁并发Atomic*类简单计数器状态标志并发容器ConcurrentHashMap高并发数据结构开发者第一次拥有了选择权可以根据场景在互斥阻塞和乐观自旋之间做出权衡。第四章双雄并立——JDK 1.6的锁优化与性能之争2006年就在开发者们逐渐转向ReentrantLock和原子类时JVM团队没有放弃synchronized。从JDK 1.6开始HotSpot VM对synchronized进行了大刀阔斧的优化引入了锁升级机制使其性能在某些场景下甚至超越了ReentrantLock。4.1 对象头与Mark Wordsynchronized的优化依赖于对象在内存中的布局。每个Java对象的对象头中都有一块称为Mark Word的区域它记录了对象的哈希码、GC年龄以及锁状态标志。锁升级的过程就是Mark Word中标志位变化的过程。4.2 锁升级路径无锁 → 偏向锁 → 轻量级锁 → 重量级锁偏向锁Biased Locking针对锁大多由同一线程获得的场景。当线程第一次进入同步块JVM会将Mark Word中的线程ID设为当前线程。此后该线程再次进入时只需比对ID几乎零开销。如果其他线程来竞争偏向锁即撤销。轻量级锁Lightweight Locking当竞争出现时锁升级为轻量级锁。线程会在自己的栈帧中创建锁记录Lock Record通过CAS自旋尝试修改Mark Word指向自己的锁记录。注意这里的CAS正是JDK 1.5引入的原子操作在JVM内部的运用——JVM自身也开始用CAS优化锁的获取。重量级锁Heavyweight Locking当自旋超过一定阈值或等待线程数过多锁会膨胀为重量级锁。此时Mark Word指向一个操作系统级别的互斥量mutex。未获取锁的线程进入阻塞队列由操作系统调度涉及用户态与内核态的切换开销最大。4.3 CAS的双重角色从这个阶段开始CAS在Java并发体系中扮演着双重角色应用层面开发者通过AtomicInteger等类直接使用CAS实现无锁并发。JVM层面虚拟机内部用CAS优化synchronized的轻量级锁获取用CAS实现偏向锁的撤销。4.4 两种路径的对比到了JDK 1.6Java实际上形成了两条并行的并发技术路线技术路线代表核心机制优势内置锁路线synchronized锁升级 JVM级CAS简单易用自动优化显式锁路线ReentrantLockAtomic*应用级CAS 自旋灵活可控功能丰富第五章巅峰对决——JDK 1.8的革命性重构2014年JDK 1.8的发布标志着Java并发技术走向成熟。这一年ConcurrentHashMap经历了革命性重构将CAS和synchronized的配合发挥到了极致。5.1 ConcurrentHashMap的两代变迁JDK 7时代分段锁Segment在JDK 7及之前ConcurrentHashMap采用了分段锁设计。它将整个哈希表分成多个Segment段每个Segment独立持有一把锁。当多个线程操作不同Segment中的数据时可以真正并行执行大大提高了并发度。但是分段锁也存在固有缺陷内存开销大需要维护多个锁段数量固定扩容困难且某些操作如size()需要依次锁住所有段性能较差。JDK 8的革新synchronized CASJDK 8完全摒弃了分段锁采用了更精细的设计数据结构采用Node数组 链表/红黑树。并发控制写入数据时根据桶位bucket的状态采取不同策略如果桶位为空则通过CAS直接插入节点——这是无锁并发的实践。如果桶位非空则对链表头节点或树根节点使用synchronized加锁——这是细粒度锁的应用。这种设计实现了锁粒度细化到单个桶且巧妙利用了synchronized在JDK 8中已优化的性能。写线程只需锁住自己正在操作的桶其他桶的访问完全不受影响。这是CAS局部锁的完美结合也是两条并发技术路线从竞争走向融合的标志。5.2 LongAdder的登场JDK 8还引入了LongAdder这是对AtomicLong的进一步优化。在高并发场景下大量线程同时CAS竞争同一个变量会导致大量重试和缓存抖动。LongAdder的核心思想是分而治之它将一个计数变量拆分成多个单元Cell每个线程只更新自己对应的单元最后求和时再汇总。这实际上是将CAS的竞争从单点分散到多点进一步提升了吞吐量。5.3 JDK 8的历史地位JDK 8标志着Java并发技术走向成熟锁不再是唯一选择大量场景可以用无锁数据结构替代锁。悲观与乐观的融合ConcurrentHashMap证明了CAS和synchronized可以协同工作。细粒度成为主流无论是锁粒度还是数据分片粒度都走向精细化。第六章未来已来——JDK 9到21的持续演进2017-2023年从JDK 9开始Java进入模块化和快速迭代的时代。并发领域虽然没有革命性变革但持续进行着优化和调整。6.1 偏向锁的谢幕JDK 15中偏向锁被标记为废弃JDK 21中偏向锁默认被禁用。原因在于在高并发应用中锁通常由不同线程竞争偏向锁的撤销成本反而成为负担。JVM团队根据实际场景数据做出了务实的选择——这体现了并发技术演进中实践检验真理的原则。6.2 VarHandle标准化的内存访问工具JDK 9引入了java.lang.invoke.VarHandle作为Unsafe的标准化替代方案解决了Unsafe安全性差、API不规范的问题同时保留了底层内存操作和原子操作的能力成为Java并发底层编程的新选择。VarHandle的核心优势的在于安全、标准化、高效其核心能力与Unsafe对应但更具规范性原子操作支持提供compareAndSet、getAndAdd、getAndSet等原子方法与Unsafe的原子操作功能一致且支持更丰富的类型包括基本类型和引用类型底层同样依赖CPU的CAS指令性能与Unsafe相当。内存语义控制支持通过memoryOrder参数指定内存访问顺序如volatile、acquire、release等灵活控制内存可见性和有序性比volatile关键字更精细也比Unsafe的操作更规范。安全性提升VarHandle是公开的标准化API无需通过反射获取且操作被严格限制在合法范围内避免了Unsafe直接操作内存可能引发的风险同时提供了类型安全检查减少了编程错误。替代Unsafe的核心场景在JDK后续版本中大量原依赖Unsafe的类如原子类、并发容器逐渐迁移到VarHandle实现例如AtomicInteger的部分方法在JDK 9中已通过VarHandle实现进一步提升了代码的安全性和可维护性。VarHandle的出现标志着Java底层并发工具的规范化演进——不再依赖非公开的危险API而是通过标准化接口提供底层能力兼顾了高性能和安全性为后续并发技术的优化奠定了基础。结语纵观这二十多年的发展Java并发安全的演进从最初粗暴的全局锁到精细化的读写锁再到基于CAS的无锁并发每一次技术跃迁都是为了在保证线程安全的前提下将硬件的并行效能发挥到极致。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2433497.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!