从互斥锁到无锁,Java 20年并发安全进化史

news2026/3/27 10:28:57
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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…