核心内容摘要
AgentCPM实战案例:金融行业研究报告自动生成
今天聊的是线程安全与锁优化这是 Java 并发编程的核心实战层内容承接线程实现与调度的基础直接解决多线程共享资源的并发安全问题—— 其中线程安全级别是衡量代码 / 容器并发安全性的标准帮你快速判断 “哪些场景天生安全、哪些场景需要手动同步”synchronized 关键字和显式锁 ReentrantLock是实现线程安全的两大核心手段前者是 JVM 原生支持的内置锁后者是 JUC 提供的灵活锁实现而锁优化尤其是 JDK
6 后 synchronized 的锁升级是 JVM 提升锁性能的关键让重量级锁不再 “重”。
本次核心要点补充底层实现、特性对比、实战坑点、锁优化细节让你不仅能区分线程安全级别、掌握两种锁的使用更能理解 “为什么 synchronized 在 JDK
6 后性能大幅提升”“实际开发中该选 synchronized 还是 ReentrantLock”。
线程安全与锁的关系线程安全的核心目标是多线程并发访问共享资源时程序的执行结果始终与单线程执行结果一致不会出现脏读、脏写、数据丢失等并发问题。
而锁是实现线程安全的最核心、最常用手段—— 通过锁对共享资源进行 “独占式访问”保证同一时间只有一个线程能操作共享资源从根本上避免多线程竞争导致的并发问题。
同时 JVM 的锁优化会在不影响线程安全的前提下尽可能减少锁的性能开销让并发程序既安全又高效。
线程安全级别、同步实现方式、锁优化三者的关系线程安全级别衡量标准→ 选择同步实现方式synchronized/ReentrantLock→ JVM锁优化提升性能
线程安全级别Java 中线程安全并非 “非黑即白”而是分为不可变、绝对线程安全、相对线程安全、线程兼容、线程对立5 个级别级别从高到低依次递减这是由并发领域专家 Doug LeaJUC 框架作者定义的标准也是我们选择并发组件、编写安全代码的重要依据。
核心判断依据是否需要开发者手动添加同步措施才能保证多线程下的执行正确性。
不可变Immutable核心定义共享资源的状态一旦创建就永远无法修改多线程只能读取不能写入因此不存在任何并发竞争问题天生线程安全无需任何同步措施。
实现方式Java 中实现不可变的核心手段用final修饰基本类型变量如final int a 1赋值后无法修改用final修饰对象保证对象引用不可变若要让对象内容不可变需结合其他手段如 String、Integer编写不可变类所有成员变量私有且 final无 setter 方法构造方法完成初始化无任何修改对象状态的方法如 String、Integer、Long 等包装类BigDecimal、BigInteger。
实战示例// 不可变类String天生线程安全 String str test; // 多线程并发调用substring、concat等方法不会出现并发问题 // 因为String的所有方法都会返回新对象不会修改原对象状态核心特性最简单、最安全的线程安全级别开发中优先使用不可变对象作为共享资源如配置信息、常量从根源上避免并发问题。
绝对线程安全Absolutely Thread-Safe无论多线程如何调度、如何调用对象的方法无需开发者添加任何同步措施对象本身始终能保证线程安全执行结果与单线程一致。
核心要求不仅保证单个方法的原子性、可见性、有序性还能保证多个方法的复合操作的线程安全 —— 这是该级别与 “相对线程安全” 的核心区别。
实战现状Java 中几乎没有真正的绝对线程安全类即使是被认为线程安全的 Vector、Hashtable也并非绝对线程安全。
反例Vector 的复合操作仍需手动同步VectorInteger vector new Vector(); // 线程A判断是否为空非空则删除最后一个元素 new Thread(() - { if (!vector.isEmpty()) { vector.remove(vector.size() -
; } }).start(); // 线程B判断是否为空非空则获取最后一个元素 new Thread(() - { if (!vector.isEmpty()) { vector.get(vector.size() -
; } }).start();问题Vector 的isEmpty()、remove()、get()都是线程安全的单个方法但 **“判断 操作” 的复合操作 ** 并非原子性 —— 线程 A 执行isEmpty()后线程 B 也执行isEmpty()此时都为 true若线程 A 先执行remove()线程 B 再执行get()会抛出ArrayIndexOutOfBoundsException。
结论绝对线程安全的实现成本极高会导致性能大幅下降实际开发中几乎不使用。
相对线程安全Relatively Thread-Safe保证对象单个方法的执行是线程安全的无需开发者为单个方法添加同步措施但多个方法的复合操作无法保证线程安全若需要复合操作的安全性需开发者手动添加同步措施。
核心特性兼顾安全性和性能是 Java 中最常用的线程安全级别JUC 中的大部分并发容器如 ConcurrentHashMap、CopyOnWriteArrayList、传统的线程安全容器如 Vector、Hashtable都属于该级别。
复合操作的线程安全保障针对上述 Vector 的复合操作问题开发者只需手动添加同步锁将复合操作封装为原子操作即可VectorInteger vector new Vector(); Object lock new Object(); // 线程A复合操作加锁 new Thread(() - { synchronized (lock) { if (!vector.isEmpty()) { vector.remove(vector.size() -
; } } }).start(); // 线程B复合操作加锁 new Thread(() - { synchronized (lock) { if (!vector.isEmpty()) { vector.get(vector.size() -
; } } }).start();
线程兼容Thread-Compatible—— 非线程安全可手动同步核心定义对象本身并非线程安全但可以通过开发者手动添加同步措施如 synchronized、ReentrantLock让其在多线程环境下安全运行。
实战示例Java 中大部分普通类 / 容器都属于该级别如 ArrayList、HashMap、LinkedList 等// 非线程安全的ArrayList手动加锁实现线程安全 ArrayListInteger list new ArrayList(); Object lock new Object(); // 多线程并发添加元素加锁保证安全 new Thread(() - { synchronized (lock) { list.add(
; } }).start();核心地位开发中最常见的级别大部分并发编程工作都是围绕 “为线程兼容的对象添加同步措施” 展开。
线程对立Thread-Antagonistic核心定义对象本身不支持多线程并发访问即使开发者添加了同步措施也无法保证多线程下的执行正确性甚至会导致程序死锁、崩溃。
核心原因这类对象的方法会直接操作底层的共享资源且依赖于线程的执行时序同步措施无法消除其底层的并发冲突。
实战示例Java 中极少出现这类对象典型的反例是修改系统全局状态的方法如System.setOut()、Thread.stop()已废弃多线程并发调用Thread.stop()会强制终止线程导致线程持有的锁无法释放引发死锁多线程并发修改系统输出流会导致输出内容乱码且同步措施无法彻底解决。
开发要求绝对避免在多线程环境下使用线程对立的对象 / 方法。
5 线程安全级别核心对比表安全级别核心特征是否需要手动同步典型示例实际开发使用频率不可变状态不可修改天生安全❌ 不需要String、Integer、final 常量✅ 极高绝对线程安全单个 / 复合操作都安全❌ 不需要几乎无Java 中无典型实现❌ 极低相对线程安全单个方法安全复合操作需同步⚠️ 复合操作需要Vector、Hashtable、ConcurrentHashMap✅ 极高线程兼容非线程安全可手动同步✅ 全部需要ArrayList、HashMap、普通 POJO✅ 极高线程对立完全不安全无法同步❌ 同步无效Thread.stop ()、部分系统级方法❌ 几乎不用
同步实现方式Java 中实现线程安全的核心同步手段是锁分为JVM 原生支持的内置锁synchronized和JUC 提供的显式锁ReentrantLock两者都是可重入锁核心特性后续解析但底层实现、功能特性、使用方式差异巨大 —— 前者简单易用、自动释放后者灵活可控、功能丰富。
先明确一个核心概念可重入锁—— 线程获取锁后再次请求获取同一把锁时不会被自己阻塞而是直接获取锁锁的计数器 1释放时计数器 - 1直到计数器为 0 才真正释放锁。
可重入锁避免了线程 “自己锁自己” 的死锁问题是现代锁的基本特性。
关键字 synchronizedsynchronized是 Java最基础、最常用的同步关键字无需手动导入包无需手动释放锁JVM 会在编译期和运行时对其进行全方位优化JDK
6 后性能大幅提升。
其核心是依赖管程Monitor实现同步编译后会生成monitorenter和monitorexit指令。
1底层实现管程Monitor又称监视器锁是 JVM 实现同步的核心底层机制每个 Java 对象都天生关联一个 Monitor 对象存储在对象头的 Mark Word 中Monitor 是独占式锁同一时间只能被一个线程持有字节码指令JVM 将synchronized代码块 / 方法编译为字节码时会在代码块开头插入monitorenter指令结尾插入monitorexit指令即使发生异常JVM 也会通过异常处理机制执行monitorexit保证锁的释放执行逻辑线程执行monitorenter时尝试获取对象关联的 Monitor 锁成功则持有锁计数器 1失败则阻塞等待线程执行完同步代码后执行monitorexit释放锁计数器 - 1若线程执行中抛出异常JVM 会自动执行monitorexit保证锁一定被释放核心优点。
2synchronized 的三种使用形式synchronized可以修饰方法和代码块共三种使用形式本质都是获取对象的 Monitor 锁只是锁对象不同public class SyncDemo { //
修饰实例方法锁对象是【当前类的实例对象】this public synchronized void instanceMethod() {} //
修饰静态方法锁对象是【当前类的Class对象】SyncDemo.class public static synchronized void staticMethod() {} //
修饰代码块锁对象是【手动指定的任意对象】推荐使用专用锁对象避免锁逃逸 public void blockMethod() { Object lock new Object(); synchronized (lock) { // 同步代码块 } } }实战建议优先使用修饰代码块的形式手动指定专用锁对象 —— 避免使用this、Class 对象作为锁易发生锁竞争更不要使用字符串常量、基本类型包装类作为锁如lock、Integer.valueOf(
因常量池复用导致锁全局唯一引发严重的锁竞争。
3核心特性可重入天生支持可重入线程获取锁后可再次获取无需额外处理非公平锁JDK
6 后默认采用非公平锁可通过 JVM 参数-XX:UseFairSync改为公平锁线程不会按等待顺序获取锁能提升锁的执行效率不可中断线程获取锁失败时会一直阻塞等待无法被中断如调用interrupt()无法唤醒阻塞的线程自动释放无论正常执行还是抛出异常JVM 都会自动执行monitorexit释放锁不会出现锁泄漏独占式同一时间只能有一个线程持有锁其他线程只能阻塞等待。
显式锁 ReentrantLock——JUC 提供灵活可控ReentrantLock是java.util.concurrent.locks.Lock接口的核心实现类译为 “可重入锁”是 JDK
5 为了弥补synchronized的不足而推出的显式锁 —— 底层基于AQS抽象队列同步器实现支持公平锁 / 非公平锁、可中断锁、尝试获取锁等高级功能使用时需要手动获取锁和释放锁。
1底层实现AQS是 JUC 框架的核心底层机制基于双向链表实现等待队列通过volatile 修饰的状态变量 state控制锁的获取与释放state0 表示锁未被持有state0 表示锁被持有state 的值为重入次数ReentrantLock通过继承 AQS实现其抽象方法完成锁的逻辑线程调用lock()时尝试将 AQS 的 state 从 0 改为 1成功则获取锁失败则加入 AQS 等待队列阻塞线程再次调用lock()时state1可重入线程调用unlock()时state-1当 state0 时真正释放锁唤醒等待队列中的下一个线程。
2核心使用方式ReentrantLock的锁释放必须手动执行且必须放在finally代码块中—— 否则若线程执行中抛出异常锁无法释放会导致锁泄漏引发其他线程永久阻塞。
标准使用示例import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockDemo { // 创建ReentrantLock对象默认非公平锁参数true为公平锁 private final ReentrantLock lock new ReentrantLock(); public void doWork() { //
手动获取锁 lock.lock(); try { // 同步代码块操作共享资源 System.out.println(操作共享资源); } finally { //
手动释放锁必须放在finally中 lock.unlock(); } } }3核心特性支持公平锁 / 非公平锁创建时通过参数指定new ReentrantLock(true)为公平锁公平锁会按线程的等待顺序分配锁非公平锁则允许线程 “插队”默认非公平锁效率更高可中断锁支持lockInterruptibly()方法线程获取锁时若被其他线程中断调用interrupt()会抛出InterruptedException并停止等待避免线程永久阻塞尝试获取锁支持tryLock()和tryLock(long timeout, TimeUnit unit)方法尝试获取锁tryLock()立即返回成功返回 true失败返回 false不阻塞tryLock(timeout)在指定时间内尝试获取锁超时则返回 false避免无限阻塞可重入与synchronized一致支持线程重入同一把锁独占式同一时间只能有一个线程持有锁。
4高级功能ReentrantLock可通过newCondition()方法创建条件变量 Condition替代Object的wait()/notify()/notifyAll()实现更灵活的线程等待 / 唤醒 —— 一个 ReentrantLock 可以创建多个 Condition实现对不同线程的精准唤醒而Object的监视器只能有一个等待队列notify()会随机唤醒一个线程。
Condition 使用示例import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class ConditionDemo { private final ReentrantLock lock new ReentrantLock(); // 创建两个条件变量分别对应“生产者等待”和“消费者等待” private final Condition producerCond lock.newCondition(); private final Condition consumerCond lock.newCondition(); private int count 0; // 共享资源库存 // 生产者方法库存满则等待否则生产并唤醒消费者 public void produce() throws InterruptedException { lock.lock(); try { while (count
{ // 库存满生产者等待 producerCond.await(); // 替代Object.wait() } count; System.out.println(生产后库存 count); consumerCond.signal(); // 唤醒消费者线程精准唤醒 } finally { lock.unlock(); } } // 消费者方法库存空则等待否则消费并唤醒生产者 public void consume() throws InterruptedException { lock.lock(); try { while (count
{ // 库存空消费者等待 consumerCond.await(); } count--; System.out.println(消费后库存 count); producerCond.signal(); // 唤醒生产者线程精准唤醒 } finally { lock.unlock(); } } }
synchronized 与 ReentrantLock 核心对比表这是并发编程的高频考点也是实际开发中选择锁的关键依据从底层实现、功能特性、使用方式等维度全面对比对比维度synchronizedReentrantLock底层实现JVM 原生Monitor 管程 字节码指令JDK 实现AQS 抽象队列同步器锁类型可重入、独占式可重入、独占式公平性默认非公平可通过 JVM 参数修改默认非公平可通过构造参数指定true 公平可中断性❌ 不可中断线程永久阻塞✅ 支持可中断lockInterruptibly ()尝试获取锁❌ 不支持只能阻塞等待✅ 支持 tryLock ()/tryLock (timeout)锁释放方式✅ JVM 自动释放正常 / 异常⚠️ 手动释放必须放 finally条件变量❌ 基于 Object仅一个等待队列✅ 基于 Condition支持多个条件变量 / 精准唤醒锁状态查询❌ 无法查询锁的持有状态✅ 支持isLocked ()、isHeldByCurrentThread ()性能JDK
6 后优化与 ReentrantLock 持平高版本与 synchronized 持平低版本更优使用复杂度✅ 简单无需手动管理⚠️ 复杂需手动加锁 / 释放易出错
什么时候用 synchronized什么时候用 ReentrantLockJDK
6 后JVM 对synchronized做了大量锁优化其性能与 ReentrantLock 基本持平因此优先使用 synchronized—— 简单易用无锁泄漏风险仅在需要ReentrantLock 的高级功能时才选择使用 ReentrantLock。
优先使用 synchronized 的场景大部分普通同步场景如修饰简单的方法 / 代码块保证单个方法 / 复合操作的线程安全追求代码简洁性、易维护性避免手动管理锁的释放无需中断锁等待、无需尝试获取锁、无需精准唤醒线程的场景。
选择 ReentrantLock 的场景需要公平锁如要求线程按等待顺序执行避免线程饥饿需要可中断的锁等待如防止线程因获取锁而永久阻塞需要尝试获取锁如非阻塞式同步获取锁失败则执行其他逻辑需要多个条件变量如生产者 - 消费者模型需要精准唤醒生产者 / 消费者需要查询锁的状态如判断当前线程是否持有锁、锁是否被其他线程持有。
锁优化你提到的 “锁优化” 是 JVM 针对synchronized的核心优化ReentrantLock 基于 AQS 实现优化由 JDK 完成JDK
6 之前synchronized 是重量级锁依赖操作系统的互斥量Mutex实现线程阻塞 / 唤醒需要内核参与性能极低JDK
6 后JVM 引入了偏向锁、轻量级锁、重量级锁的锁升级机制并结合锁消除、锁粗化等优化手段让 synchronized 的性能大幅提升成为高效的同步手段。
锁优化的核心目标在不影响线程安全的前提下尽可能减少锁的开销避免线程的内核态阻塞—— 锁的升级过程是不可逆的只能从偏向锁→轻量级锁→重量级锁不能降级保证锁的优化不会破坏线程安全。
锁升级 —— 偏向锁 → 轻量级锁 → 重量级锁锁升级的核心依据是多线程对锁的竞争程度无竞争 → 偏向锁最低开销几乎无性能损耗轻度竞争多个线程交替获取锁无同时竞争→ 轻量级锁自旋锁避免内核阻塞重度竞争多个线程同时获取锁→ 重量级锁依赖操作系统保证线程安全。
锁的状态存储在Java 对象头的 Mark Word中Mark Word 是对象头的核心部分用于存储对象的哈希码、锁状态、GC 标记等信息JVM 通过修改 Mark Word 的锁状态位完成锁的升级。
1无竞争时的最优解消除锁的获取开销核心场景单个线程多次获取同一把锁无任何其他线程竞争 —— 这是开发中最常见的场景如单线程执行同步代码。
核心原理偏向锁会偏向于第一个获取锁的线程该线程后续再次获取锁时无需任何同步操作只需判断 Mark Word 中的偏向线程 ID 是否为当前线程直接获取锁消除了锁的获取开销。
执行逻辑线程第一次获取锁时JVM 将对象头 Mark Word 的锁状态改为偏向锁并记录当前线程的 ID该线程后续再次获取锁时只需判断 Mark Word 中的线程 ID 是否为自身是则直接获取锁无需任何操作若有其他线程尝试获取锁偏向锁会被撤销升级为轻量级锁。
核心优点几乎无性能损耗适用于单线程重复获取锁的场景。
2轻度竞争时的自旋锁避免内核阻塞核心场景多个线程交替获取同一把锁无同时竞争即一个线程释放锁后另一个线程才尝试获取锁—— 轻度竞争场景。
核心原理轻量级锁基于自旋锁实现 —— 线程获取锁失败时不会立即进入内核态阻塞而是在用户态自旋循环尝试获取锁等待持有锁的线程快速释放锁避免内核态 / 用户态的切换开销内核切换的开销远大于自旋的开销。
执行逻辑偏向锁撤销后JVM 将对象头 Mark Word 的锁状态改为轻量级锁线程获取锁失败时进入自旋等待默认自旋 10 次可通过 JVM 参数-XX:PreSpinLoop调整若自旋期间获取到锁持有锁的线程快速释放则正常执行若自旋结束仍未获取到锁轻量级锁升级为重量级锁线程进入内核态阻塞。
核心优点避免了轻度竞争下的内核阻塞提升了锁的性能。
3重度竞争时的兜底方案保证线程安全核心场景多个线程同时获取同一把锁存在激烈的竞争 —— 重度竞争场景。
核心原理重量级锁是 JDK
6 之前 synchronized 的原始实现依赖操作系统的互斥量Mutex实现线程获取锁失败时会进入内核态的阻塞队列由操作系统负责线程的阻塞 / 唤醒保证同一时间只有一个线程持有锁。
执行逻辑轻量级锁升级后JVM 将对象头 Mark Word 的锁状态改为重量级锁并关联操作系统的互斥量线程获取锁失败时放弃自旋由操作系统将其挂起加入内核阻塞队列持有锁的线程释放锁时操作系统唤醒阻塞队列中的一个线程使其尝试获取锁。
核心缺点线程阻塞 / 唤醒需要内核参与性能开销大核心优点能处理重度竞争场景保证线程安全。
锁升级完整流程示意图flowchart LR A[无竞争] --|单个线程获取锁| B[偏向锁br/记录线程ID重复获取无开销] B --|其他线程尝试获取锁| C[轻量级锁br/自旋锁轻度竞争自旋等待] C --|自旋失败/重度竞争| D[重量级锁br/操作系统互斥量内核阻塞] note B-C: 偏向锁撤销不可逆 note C-D: 轻量级锁升级不可逆
锁消除 —— 消除无竞争的锁避免冗余开销核心原理JVM 通过逃逸分析之前讲过的核心优化技术判断锁对象是否无逃逸仅在当前线程内使用无线程竞争若确定无竞争会自动消除该锁避免无意义的同步开销。
核心场景局部锁对象无逃逸如方法内创建的锁对象、StringBuffer 的局部对象StringBuffer 的方法有 synchronized 修饰但局部对象无逃逸锁会被消除。
// 锁消除示例StringBuffer的append方法有synchronized但局部对象无逃逸锁会被消除 public String buildString() { StringBuffer sb new StringBuffer(); // 无逃逸的局部对象 sb.append(a).append(b).append(c); return sb.toString(); }优化前每次调用append()都会获取 sb 对象的锁优化后JVM 通过逃逸分析判断 sb 无逃逸消除所有 synchronized 锁直接执行 append 的核心逻辑无锁开销。
锁粗化 —— 合并连续的锁操作减少锁的获取 / 释放开销核心原理若线程连续多次获取 / 释放同一把锁如循环内的同步代码块JVM 会将连续的锁操作合并为一次即扩大同步代码块的范围减少锁的获取 / 释放次数避免频繁的锁操作开销。
核心场景循环内的同步代码块最典型连续多次调用带同步的方法。
// 锁粗化前循环内每次迭代都获取/释放锁共1000次锁操作 public void loopSync(Object lock) { for (int i 0; i 1000; i) { synchronized (lock) { // 简单的同步操作 } } } // 锁粗化后JVM将锁操作合并为一次仅1次获取/释放锁 public void loopSyncOpt(Object lock) { synchronized (lock) { for (int i 0; i 1000; i) { // 简单的同步操作 } } }优化效果将 1000 次锁获取 / 释放操作减少为 1 次大幅降低锁的开销。
注意点锁粗化不会无限扩大同步代码块的范围 ——JVM 会保证合并后的同步代码块不会出现严重的锁竞争否则会导致其他线程长时间阻塞。
线程安全与锁
使用字符串 / 基本类型包装类作为 synchronized 的锁对象// 错误字符串常量池复用导致锁全局唯一引发严重锁竞争 synchronized (lock) {} // 错误Integer常量池复用-128~127锁对象并非唯一 synchronized (Integer.valueOf(
) {}正确做法使用专用的 Object 对象或this/Class 对象作为锁避免锁对象被复用。
ReentrantLock 忘记在 finally 中释放锁// 错误若doWork()抛出异常lock.unlock()不会执行导致锁泄漏 lock.lock(); doWork(); lock.unlock();正确做法必须将 lock.unlock () 放在 finally 代码块中保证锁一定被释放。
认为 volatile 能保证复合操作的原子性// 错误volatile只能保证可见性和有序性无法保证i的原子性 volatile int i 0; i; // 复合操作读取→加1→赋值多线程下会出现数据丢失正确做法使用synchronized/ReentrantLock保证复合操作的原子性或使用AtomicInteger等原子类。
公平锁一定比非公平锁好公平锁按线程等待顺序分配锁避免了线程饥饿但性能远低于非公平锁—— 因为公平锁需要维护等待队列的顺序且禁止线程 “插队”导致锁的获取效率降低。
正确做法默认使用非公平锁仅在需要避免线程饥饿的特殊场景下才使用公平锁。
自旋锁越多越好自旋锁适用于轻度竞争场景若自旋次数过多会导致CPU 空转浪费 CPU 资源若自旋次数过少无法发挥自旋锁的优势。
正确做法使用 JVM 默认的自旋次数JDK
6 为自适应自旋JVM 会根据历史竞争情况动态调整自旋次数无需手动修改。
最后小结线程安全 5 个级别从高到低为不可变、绝对线程安全、相对线程安全、线程兼容、线程对立不可变和相对线程安全是开发中最常用的级别线程对立绝对避免使用复合操作的线程安全需要开发者手动同步。
两大核心同步锁synchronizedJVM 原生内置锁基于 Monitor 管程实现编译为monitorenter/monitorexit指令自动释放锁简单易用JDK
6 后经锁优化性能大幅提升ReentrantLockJUC 显式锁基于 AQS 实现支持公平锁 / 非公平锁、可中断锁、尝试获取锁、多条件变量需手动加锁 / 释放必须放 finally功能丰富适用于复杂同步场景。
锁选择原则JDK
6 后优先使用 synchronized仅在需要 ReentrantLock 的高级功能时才选择使用两者都是可重入锁性能基本持平。
JVM 核心锁优化针对 synchronized锁升级偏向锁无竞争→ 轻量级锁轻度竞争自旋→ 重量级锁重度竞争内核阻塞不可逆根据竞争程度动态调整锁消除通过逃逸分析消除无竞争的锁避免冗余开销锁粗化合并连续的锁操作减少锁的获取 / 释放次数。
核心实战要点避免使用字符串 / 包装类作为锁对象防止锁复用ReentrantLock 的释放必须放在 finally 中避免锁泄漏优先使用非公平锁自适应自旋锁无需手动调整volatile 无法保证复合操作的原子性需结合锁或原子类使用。
线程安全与锁优化是 Java 并发编程的实战核心掌握这部分内容你就能编写出既安全又高效的并发代码解决大部分实际开发中的并发问题 —— 后续的并发容器、线程池等 JUC 组件都是基于这些锁机制和优化手段实现的。