当前位置: 首页 > news >正文

synchronized和RentrantLock用哪个?

在现代 Java 版本(JDK 8 及以后),synchronizedReentrantLock 在大多数常规场景下的性能已经非常接近,甚至在某些情况下 synchronized 表现更好。

因为 synchronized 在 JDK 6 之后引入了偏向锁、轻量级锁和自适应自旋等优化,才使得它的性能得到了质的飞跃,不再是过去那个“重量级”的代名词。


我们来深入探讨一下:

1. synchronized 的演进与优化

在 JDK 6 之前,synchronized 被认为是重量级锁,因为它依赖于操作系统的互斥量(Mutex),涉及到用户态和内核态的切换,开销较大。

但从 JDK 6 开始,JVM 对 synchronized 进行了大量的优化,引入了锁升级机制:

  • 无锁状态: 初始状态,没有竞争。

  • 偏向锁 (Biased Locking):

    • 思想: 如果一个线程反复进入同一个同步块,那么它就不需要每次都进行加锁和解锁的操作。JVM 会“偏向”这个线程,认为它会一直持有这个锁。
    • 原理: 当一个线程第一次获取锁时,JVM 会把锁对象的 Mark Word(对象头的一部分)设置为偏向模式,并记录下这个线程的ID。如果后续还是这个线程来获取锁,它只需要检查Mark Word中的线程ID是否是自己,如果是,就直接进入同步块,无需任何同步操作。
    • 适用场景: 几乎没有竞争的场景。
    • 效率: 极高,几乎没有开销。
  • 轻量级锁 (Lightweight Locking):

    • 思想: 如果有少量线程在短时间内交替竞争同一个锁,但没有发生线程阻塞(即没有线程进入等待状态),那么就不需要升级到重量级锁。
    • 原理: 当偏向锁失效(有其他线程尝试获取锁)时,JVM 会在当前线程的栈帧中创建锁记录(Lock Record),并将锁对象的Mark Word复制到锁记录中,然后尝试使用 CAS (Compare-And-Swap) 操作将Mark Word更新为指向锁记录的指针。如果成功,则获取锁。如果失败,说明有竞争,但如果竞争不激烈,会尝试自旋。
    • 适用场景: 线程交替执行同步块,竞争不激烈。
    • 效率: 较高,避免了用户态/内核态切换。
  • 自适应自旋 (Adaptive Spinning):

    • 思想: 在轻量级锁竞争失败后,线程不会立即阻塞,而是会“自旋”一段时间(空循环),看看持有锁的线程是否很快释放锁。如果自旋成功(锁很快被释放),就避免了线程的上下文切换。自旋的次数不是固定的,而是根据上次自旋的成功率和锁持有者的状态动态调整。
    • 适用场景: 竞争不激烈,锁持有时间短。
    • 效率: 进一步提升轻量级锁的性能。
  • 重量级锁 (Heavyweight Locking):

    • 思想: 如果竞争非常激烈,自旋也无法获取到锁,或者持有锁的线程执行时间过长,导致其他线程长时间自旋,那么就会升级为重量级锁。
    • 原理: 依赖操作系统的互斥量,线程会进入阻塞状态,涉及到用户态和内核态的切换。
    • 适用场景: 竞争激烈,线程需要阻塞等待。
    • 效率: 最低,但能保证正确性。

2. ReentrantLock

ReentrantLockjava.util.concurrent.locks 包下的一个类,它是基于 AQS (AbstractQueuedSynchronizer) 实现的。

核心思想: “我给你提供更灵活的锁操作,你可以手动控制加锁和解锁,还可以尝试非阻塞地获取锁,或者设置超时时间。”

特点:

  • 手动加锁/解锁: 必须手动调用 lock()unlock() 方法,容易忘记释放锁导致死锁。
  • 可中断锁: lockInterruptibly() 方法允许在等待锁的过程中被中断。
  • 尝试非阻塞获取锁: tryLock() 方法可以尝试获取锁,如果失败立即返回,不会阻塞。
  • 公平锁/非公平锁: 可以选择公平锁(按请求顺序获取锁)或非公平锁(抢占式获取锁,默认非公平,性能更高)。
  • 条件变量: 可以通过 newCondition() 创建多个条件变量,实现更复杂的线程间协作。

效率:

  • ReentrantLock 内部也是通过 CAS 操作来实现锁的获取和释放,避免了用户态/内核态的切换(除非竞争激烈到需要阻塞)。
  • 在竞争不激烈的情况下,它的性能与 synchronized 的轻量级锁类似。
  • 在竞争激烈的情况下,它会通过 AQS 的队列机制来管理等待线程,性能也很好。

3. 性能对比与选择

特性/锁类型synchronizedReentrantLock
实现方式JVM 原生支持,字节码指令(monitorenter/exit)Java API,基于 AQS (AbstractQueuedSynchronizer)
锁升级自动升级/降级(偏向 -> 轻量 -> 重量)无锁升级概念,直接使用 CAS 和 AQS 队列
加锁/解锁自动(编译器插入)手动(lock() / unlock()),必须在 finally 块中释放
灵活性较低,功能固定较高,提供更多高级功能(可中断、尝试锁、公平/非公平、条件变量)
公平性非公平可选公平/非公平
性能JDK 8+ 大多数场景下与 ReentrantLock 相当,甚至更好在特定高级功能需求下有优势,常规场景与 synchronized 相当
可重入性
异常处理自动释放锁必须在 finally 块中手动释放,否则可能死锁

为什么说 synchronized 在某些情况下可能更好?

  1. JVM 优化: synchronized 是 JVM 原生支持的,JVM 可以对其进行更深层次的优化,例如逃逸分析、锁消除、锁粗化等。这些优化是 ReentrantLock 无法享受到的。
  2. 自动管理: synchronized 的自动加锁和解锁机制,使得代码更简洁,不易出错。而 ReentrantLock 需要手动管理,一旦忘记 unlock(),就可能导致死锁。
  3. 偏向锁的优势: 在几乎没有竞争的场景下,synchronized 的偏向锁性能是最高的,因为它几乎没有开销。而 ReentrantLock 即使在无竞争情况下,也需要执行 CAS 操作。

什么时候选择 ReentrantLock

尽管 synchronized 性能已大幅提升,但 ReentrantLock 仍然有其不可替代的优势:

  • 需要尝试非阻塞地获取锁: tryLock()
  • 需要可中断地获取锁: lockInterruptibly()
  • 需要设置获取锁的超时时间: tryLock(long timeout, TimeUnit unit)
  • 需要实现公平锁: new ReentrantLock(true)
  • 需要多个条件变量进行线程协作: newCondition()

对于大多数简单的同步需求,优先使用 synchronized。它代码简洁,由 JVM 自动管理,并且在现代 JVM 中性能已经非常优秀。

只有当你需要 ReentrantLock 提供的高级功能(如可中断锁、非阻塞获取锁、公平锁、多条件变量)时,才考虑使用 ReentrantLock

这两种锁的演进,体现了计算机科学中一个重要的思想:“针对特定场景进行优化”

  • synchronized 的优化,是从“悲观锁”的重量级实现,通过分析实际运行时的竞争模式(无竞争、少量竞争、激烈竞争),逐步引入了“乐观锁”的思想(偏向锁、轻量级锁的 CAS),以及“自适应”的思想(自适应自旋),从而在不改变语义的前提下,大幅提升了性能。它是一种**“由内而外”**的优化,由 JVM 自动完成。
  • ReentrantLock 则是提供了一种**“由外而内”**的控制能力,它把锁的更多细节暴露给开发者,让开发者可以根据业务需求,更精细地控制锁的行为。
http://www.lryc.cn/news/615229.html

相关文章:

  • leetcode 49. 字母异位词分组 - java
  • 视图是什么?有什么用?什么时候用?MySQL中的视图
  • 深入理解强化学习:近端策略优化(PPO)算法详解
  • 阿里云 ECS 怎么用 nginx 部署80端口多个网站
  • 组件通信的方式
  • Docker容器部署Tomcat线上商城
  • Leetcode——556. 下一个更大元素 III
  • 八、《DaaS(设备即服务):企业轻资产化新路径》--从97.4%首期投入削减到AI算力高效迭代的范式革命
  • Spring 框架中提供Aware接口,实现感知容器对象
  • spring.config.import 不存在
  • “高大上“的SpringCloud?(微服务体系入门)
  • ELK常见的问题
  • 智能机票助手-接入Ollama本地模型-Spring-AI-Alibaba
  • 在ubuntu服务器下安装cuda和cudnn(笔记)
  • 揭秘MyBatis核心类MappedStatement
  • 多模态RAG赛题实战--Datawhale AI夏令营
  • 如何分析需求的可行性
  • 生产环境某业务服务JVM调优总结
  • 避免在微信小程序中频繁使用setData方法
  • 扩散LLM推理新范式:打破生成长度限制,实现动态自适应调节
  • 机器学习——09 聚类算法
  • BGP 协议笔记
  • 使用qemu运行与GDB调试内核
  • 微软推出革命性AI安全工具Project IRE,重塑网络安全防御新范式
  • 用天气预测理解分类算法-从出门看天气到逻辑回归
  • Kubernetes(K8s)不同行业的典型应用场景及价值分析 原创
  • windows、linux应急响应入侵排查
  • Qdrant Filtering:must / should / must_not 全解析(含 Python 实操)
  • 【2025】Datawhale AI夏令营-多模态RAG-Task1、Task2笔记-任务理解与Baseline代码解读
  • 金融通用智能体(Financial General Agent, FGA)的端到端解决方案