目录
[一、核心基础:什么是 CAS?](#一、核心基础:什么是 CAS?)
[1. CAS 定义](#1. CAS 定义)
[2. CAS 工作原理](#2. CAS 工作原理)
[3. CAS 的优缺点](#3. CAS 的优缺点)
[4. CAS 的应用](#4. CAS 的应用)
[二、核心锁:synchronized 全阶段优化](#二、核心锁:synchronized 全阶段优化)
[1. 锁优化的核心目标](#1. 锁优化的核心目标)
[2. synchronized 锁升级全流程](#2. synchronized 锁升级全流程)
[3. synchronized 其他优化手段](#3. synchronized 其他优化手段)
[4. 锁升级总结](#4. 锁升级总结)
[三、Java 主流锁策略分类总结](#三、Java 主流锁策略分类总结)
[1. 乐观锁 vs 悲观锁](#1. 乐观锁 vs 悲观锁)
[2. 公平锁 vs 非公平锁](#2. 公平锁 vs 非公平锁)
[3. 可重入锁 vs 不可重入锁](#3. 可重入锁 vs 不可重入锁)
[4. 共享锁 vs 排他锁](#4. 共享锁 vs 排他锁)
[5. 分段锁](#5. 分段锁)
[6. 自旋锁](#6. 自旋锁)
[四、CAS 与 synchronized 核心对比](#四、CAS 与 synchronized 核心对比)
在 Java 高并发编程中,锁 是解决线程安全问题的核心手段,从早期简单粗暴的synchronized,到无锁化的 CAS 算法,再到 JDK 对锁的层层优化,最终形成了一套完整的锁策略体系。本文将系统化梳理CAS 原理 、synchronized 锁优化全流程 以及Java 主流锁策略,帮你彻底吃透 Java 锁机制。
一、核心基础:什么是 CAS?
1. CAS 定义
CAS(Compare And Swap,比较并交换)是一种无锁乐观并发算法 ,无需加锁即可实现线程安全的原子操作,是 Java 并发包(java.util.concurrent)的核心基石。
2. CAS 工作原理
CAS 包含三个核心参数:
- V:内存中要修改的变量值(主内存值)
- A:线程读取到的旧预期值
- B:线程准备写入的新值
执行流程:
- 线程先读取变量值作为预期值 A;
- 执行计算,得到新值 B;
- 原子性执行比较:若内存中的
V == A(说明期间无其他线程修改),则将V更新为B;- 若
V != A(说明期间被其他线程修改),则操作失败,线程可重试(自旋)。
整个过程是硬件级别的原子操作,不涉及线程阻塞,效率远高于重量级锁。
3. CAS 的优缺点
- 优点:无锁、无线程阻塞 / 唤醒开销,高并发下性能优于悲观锁;
- 缺点 :
- ABA 问题 :变量从 A→B→A,CAS 无法感知变化(解决方案:加版本号 / 时间戳,如
AtomicStampedReference);- 长时间自旋开销:并发冲突高时,线程反复重试会占用 CPU;
- 仅支持单个变量的原子操作,无法保证代码块的原子性。
4. CAS 的应用
Java 中的原子类(AtomicInteger、AtomicBoolean)、ConcurrentHashMap、线程池等核心组件,底层均基于 CAS 实现。
二、核心锁:synchronized 全阶段优化
synchronized是 Java 原生的悲观互斥锁 ,早期是重量级锁(性能极低),JDK1.6 后 JVM 对其进行了全方位优化,形成了无锁→偏向锁→轻量级锁→重量级锁的链式升级流程(锁只能升级,不能降级)。
1. 锁优化的核心目标
减少用户态与内核态的切换(线程阻塞 / 唤醒依赖操作系统,开销极大),尽可能在用户态完成线程同步。
2. synchronized 锁升级全流程
(1)无锁状态
- 场景:没有线程竞争锁对象;
- 原理:对象头无任何锁标记,线程直接执行代码。
(2)偏向锁(第一个线程获取锁)
- 场景:只有一个线程反复获取锁,无竞争;
- 原理:JVM 会在对象头中记录持有锁的线程 ID,该线程后续再次获取锁时,无需任何同步操作,直接放行;
- 优势:消除无竞争场景下的锁开销,是最轻量的锁。
(3)轻量级锁(多个线程交替竞争)
- 场景:多个线程交替获取锁,无长时间阻塞;
- 原理:偏向锁遇到竞争时,立即升级为轻量级锁,线程通过CAS 自旋尝试获取锁,不阻塞线程;
- 优势:避免线程阻塞,适合短时间、低冲突的并发场景。
(4)重量级锁(多线程激烈竞争)
- 场景:多个线程同时争抢锁,自旋多次失败;
- 原理:轻量级锁升级为重量级锁,线程获取锁失败会进入阻塞队列,由操作系统调度;
- 特点:线程阻塞,无 CPU 空转开销,但切换成本最高。
3. synchronized 其他优化手段
- 锁消除:JVM 通过逃逸分析,判断锁对象仅被单线程使用,自动删除无用锁;
- 锁粗化:JVM 将多次连续的加锁 / 解锁操作,合并为一次加锁,减少开销;
- 自旋锁:线程获取锁失败时,不立即阻塞,循环重试(配合轻量级锁使用)。
4. 锁升级总结
| 锁状态 | 适用场景 | 核心原理 | 性能 |
|---|---|---|---|
| 偏向锁 | 单线程无竞争 | 记录线程 ID,无同步操作 | 最高 |
| 轻量级锁 | 线程交替竞争 | CAS 自旋,用户态执行 | 高 |
| 重量级锁 | 多线程激烈竞争 | 操作系统阻塞线程 | 低 |
三、Java 主流锁策略分类总结
除了synchronized的锁升级,Java 还根据不同维度划分了多种锁策略:
1. 乐观锁 vs 悲观锁
- 悲观锁 :默认线程会发生竞争,先加锁再操作 ,线程阻塞(如
synchronized、ReentrantLock); - 乐观锁 :默认无竞争,先操作再验证,无锁(如 CAS 算法)。
2. 公平锁 vs 非公平锁
- 公平锁 :线程按申请顺序获取锁,无饥饿(如
new ReentrantLock(true)); - 非公平锁 :线程可 "插队" 获取锁,吞吐量更高(如
synchronized、默认ReentrantLock)。
3. 可重入锁 vs 不可重入锁
- 可重入锁 :同一线程可重复获取自己持有的锁,避免死锁(
synchronized、ReentrantLock); - 不可重入锁:线程重复获取锁会阻塞,易死锁。
4. 共享锁 vs 排他锁
- 排他锁(写锁) :同一时间只允许一个线程持有(
synchronized、ReentrantLock); - 共享锁(读锁) :多个线程可同时持有,仅允许读操作(如
ReentrantReadWriteLock的读锁)。
5. 分段锁
- 原理:将数据分段,每段加独立锁,减少锁竞争;
- 应用:JDK1.7 的
ConcurrentHashMap,大幅提升并发读写效率。
6. 自旋锁
- 原理:线程获取锁失败时,循环重试而非阻塞,减少线程切换开销;
- 缺点:高并发下占用 CPU,适合短任务场景。
四、CAS 与 synchronized 核心对比
表格
| 特性 | CAS | synchronized |
|---|---|---|
| 锁类型 | 乐观锁、无锁 | 悲观锁、有锁 |
| 原子性 | 单个变量原子操作 | 代码块 / 方法原子操作 |
| 线程状态 | 无阻塞,自旋 | 竞争时阻塞 |
| 性能 | 低冲突:极高;高冲突:一般 | 低冲突:低;高冲突:稳定 |
| 适用场景 | 原子变量、短时间操作 | 代码块、复杂业务逻辑 |
| 核心问题 | ABA、自旋开销 | 锁竞争、线程阻塞 |
五、使用场景总结
- 简单原子变量操作 :优先用CAS + 原子类(无锁高性能);
- 低并发、代码简洁优先 :使用synchronized(JVM 自动优化,无需手动管理);
- 高并发、灵活控制锁 :使用ReentrantLock(可公平、可超时、可中断);
- 读多写少场景 :使用读写锁 (
ReentrantReadWriteLock); - 高并发集合 :使用ConcurrentHashMap(CAS + 分段锁 / 红黑树)。
总结
- CAS 是无锁乐观算法,靠比较并交换实现原子操作,是 Java 并发的底层核心;
- synchronized 经过 JDK 优化,形成无锁→偏向锁→轻量级锁→重量级锁的升级流程,大幅提升性能;
- Java 锁策略围绕乐观 / 悲观、公平 / 非公平、共享 / 排他等维度划分,需根据场景选择最优方案;
- 实际开发中,优先用无锁 CAS,其次用优化后的 synchronized,复杂场景用显式锁,兼顾性能与线程安全。