news 2026/8/14 0:02:12

【synchronized 与 ReentrantLock + Condition 的深度对比 Plus版】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【synchronized 与 ReentrantLock + Condition 的深度对比 Plus版】

Java并发编程:synchronized 与 ReentrantLock + Condition 的深度对比——从Monitor队列到惊群效应与精确唤醒

  • 前言
  • 正文
    • 一、每个Java对象天生都能当锁?Monitor的底层结构
      • 形象比喻:锁对象就像一个会议室。
      • 关键区别:
    • 二、synchronized的线程状态流转全过程
      • 完整过程演绎
        • 1. 抢锁阶段:
        • 2. 条件不满足,主动等待:
        • 3. 被通知唤醒:
      • 为什么被唤醒后还要再抢锁?
      • 惊群效应的根源:
    • 三、ReentrantLock + Condition的高明之处:按等待性质分类的多个队列
    • 四、生产者-消费者完整代码对比
    • 五、为什么JDK的高并发容器都用Condition?
  • 总结:从synchronized到Condition的进化

前言

最近和很多读者深入交流了Java多线程中两个核心的等待/通知机制:synchronized + wait/notify 和 ReentrantLock + Condition。很多同学在学习时都会疑惑:为什么Condition更高级?为什么高并发容器都用Condition?为什么synchronized容易出现“惊群效应”?

今天我们把这些问题全部串起来,用最通俗易懂的语言,从底层实现讲起,一步步把Monitor的两个队列、线程状态流转、惊群效应、Condition的精确唤醒全部讲清楚。读完这篇,你会对Java并发等待/通知机制有彻底的理解!

正文

一、每个Java对象天生都能当锁?Monitor的底层结构

先来一个基础但超级重要的知识点:Java中每个对象都可以作为锁对象,因为它们都继承了Object类的wait()、notify()、notifyAll()方法。
当一个对象第一次被用作锁(比如进入synchronized(obj)块)时,JVM会给它分配一个叫Monitor(对象监视器)的结构。Monitor就像这个对象的“管家”,里面主要有三样东西:

  • Owner:当前持有锁的线程(锁的“主人”)。只有Owner才能执行临界区代码。
  • Entry List(入口等待队列):还没抢到锁的线程被park(阻塞)在这里,等着锁释放后一起竞争。
  • Wait Set(等待队列):已经抢到锁,但业务条件不满足,主动调用wait()的线程,会被放到这里深度睡眠。

形象比喻:锁对象就像一个会议室。

  • Owner:正在会议室里开会的人。
  • Entry List:门口排队的想进来开会的人(抢不到门票)。
  • Wait Set:已经进过会议室,但发现“现在没内容可讨论”,主动出去休息室睡觉的人。

关键区别:

  1. Entry List里的线程:是因为抢不到锁而阻塞。
  2. Wait Set里的线程:是抢到了锁,但条件不满足,主动释放锁去睡觉。

二、synchronized的线程状态流转全过程

我们用生产者-消费者场景来一步步看线程是怎么流动的。

完整过程演绎

1. 抢锁阶段:

线程A先进入synchronized(obj),成功抢到锁 → 成为Owner,进入临界区。

其他线程(B、C、D)也想进来,发现锁被占了 → 被park到Entry List,在门口排队。

2. 条件不满足,主动等待:

线程A在临界区发现“缓冲区满了,没地方放东西”。

调用obj.wait():

立即释放锁(Owner变为null,会议室开门)。

自己走进Wait Set(休息室)睡觉,完全不参与抢锁。

锁释放后,Entry List的线程(B、C、D)被唤醒,开始竞争锁。

3. 被通知唤醒:

假设线程B抢到锁,消费后调用obj.notify()或notifyAll()。

notify():从Wait Set随机选一个线程唤醒。

notifyAll():把Wait Set里所有线程都唤醒。

超级重要!被唤醒的线程不会立刻拿到锁!

它们会从Wait Set转移到Entry List。

然后和Entry List里其他线程一起重新竞争锁。

只有抢到锁后,才能继续执行wait()后面的代码。

为什么被唤醒后还要再抢锁?

  • wait()必须在持有锁的情况下调用,从wait()恢复也必须重新持有锁,确保条件检查和修改是原子的。
    这也是为什么要用while检查条件(防止虚假唤醒),而不是if。

惊群效应的根源:

  1. synchronized只有一个Wait Set,所有等待原因的线程(生产者因为“满”、消费者因为“空”)都挤在这里。
  2. 一次notifyAll() → 把所有人转移到Entry List → 所有人抢锁 → 大部分醒来发现条件不满足,又wait()回去 → 大范围惊群,大量无谓的上下文切换,性能很差。

三、ReentrantLock + Condition的高明之处:按等待性质分类的多个队列

  1. Java为了解决synchronized的痛点,引入了java.util.concurrent.locks包。ReentrantLock基于AQS(AbstractQueuedSynchronizer),底层更灵活。

  2. AQS有一个同步队列(类似Entry List,用来排队等锁)。
    lock.newCondition()可以创建多个Condition对象,每个Condition都有自己独立的等待队列(类似Wait Set,但可以有多个)。

JavaLock lock = new ReentrantLock();
Condition notFull = lock.newCondition(); // 生产者专用:缓冲区满时等待
Condition notEmpty = lock.newCondition(); // 消费者专用:缓冲区空时等待

关键点:不是每个线程一个队列,而是按“等待性质”分类

一个Condition队列里可以有多个线程,但这些线程的等待原因是相同的(同一种性质)。
比如:notFull队列里可能有50个生产者在等(都因为缓冲区满)。
notEmpty队列里可能有30个消费者在等(都因为缓冲区空)。

await/signal的工作原理:

await():释放锁 → 进入这个Condition的专属队列睡觉。

signal():从这个队列头部取一个线程 → 转移到AQS同步队列 → 去抢锁(和synchronized唤醒后去Entry List一样)。

signal()默认只唤醒一个(先进先出),signalAll()唤醒整个队列(但只限同类线程)。

惊群范围大幅缩小:

  1. 消费者消费后,只调用notEmpty.signal() → 只唤醒notEmpty队列里的消费者。
    生产者的50个线程完全不受影响,继续睡觉。
  2. 即使signalAll(),也只影响同类线程,远比synchronized的“唤醒所有人”好太多。
    加上signal()默认只醒一个,在大多数场景几乎杜绝了惊群。

“遥控器”比喻:Condition对象就像一个遥控器,你用哪个遥控器,就操作哪个队列,完全隔离。

四、生产者-消费者完整代码对比

synchronized版本(容易大范围惊群)

JavapublicclassBuffer{privatefinal Object lock=newObject();privatefinal Queue<Integer>queue=newLinkedList<>();privatefinal int capacity=10;publicvoidproduce(int item)throws InterruptedException{synchronized(lock){while(queue.size()==capacity){lock.wait();// 所有生产者进入同一个Wait Set}queue.offer(item);lock.notifyAll();// 唤醒所有人:生产者+消费者 → 大惊群}}publicintconsume()throws InterruptedException{synchronized(lock){while(queue.isEmpty()){lock.wait();// 所有消费者也进同一个Wait Set}int item=queue.poll();lock.notifyAll();// 同上returnitem;}}}

ReentrantLock + Condition版本(按性质分类,精确唤醒)

JavapublicclassBuffer{privatefinal Lock lock=newReentrantLock();privatefinal Condition notFull=lock.newCondition();// 生产者队列privatefinal Condition notEmpty=lock.newCondition();// 消费者队列privatefinal Queue<Integer>queue=newLinkedList<>();privatefinal int capacity=10;publicvoidproduce(int item)throws InterruptedException{lock.lock();try{while(queue.size()==capacity){notFull.await();// 只进生产者专属队列}queue.offer(item);notEmpty.signal();// 只唤醒一个消费者(推荐)}finally{lock.unlock();}}publicintconsume()throws InterruptedException{lock.lock();try{while(queue.isEmpty()){notEmpty.await();// 只进消费者专属队列}int item=queue.poll();notFull.signal();// 只唤醒一个生产者}finally{lock.unlock();}}}

新版本代码更清晰、性能更高。

五、为什么JDK的高并发容器都用Condition?

打开JDK源码:

ArrayBlockingQueue、LinkedBlockingQueue、ConcurrentHashMap(部分)等底层几乎全是用ReentrantLock + Condition。

原因:

多个独立队列 + 精确唤醒
支持超时等待(awaitNanos)、可中断、公平锁等高级特性
在高并发下性能远超synchronized

总结:从synchronized到Condition的进化

  1. synchronized + wait/notify:简单易用,但只有一个Wait Set,所有线程混在一起。wait()释放锁进Wait Set,notify后转移到Entry List再抢锁,notifyAll()容易造成大范围惊群。
  2. ReentrantLock + Condition:按等待性质分类多个队列,一个队列里多个同类线程。await/signal操作独立,signal()默认只醒一个,惊群范围大幅缩小,几乎杜绝。

推荐:简单场景用synchronized,复杂等待条件(如生产者-消费者、读写锁)强烈建议用Lock + Condition。

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/c5721d2340f843d7bddc55fcf6369142.jpeg#pic_center

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/13 11:01:41

受控组件 vs 非受控组件

核心区别受控组件 (Controlled Components)由 React 完全控制&#xff0c;表单数据由 React 组件的 state 管理。非受控组件 (Uncontrolled Components)由 DOM 自身管理&#xff0c;通过 ref 获取表单值。详细对比特性受控组件非受控组件数据管理React stateDOM值更新onChange …

作者头像 李华
网站建设 2026/8/13 1:14:56

HTTPS 原理详解

1. HTTPS 是什么&#xff1f;HTTPS HTTP SSL/TLSHTTP&#xff1a;超文本传输协议&#xff08;明文传输&#xff09;SSL/TLS&#xff1a;安全套接层/传输层安全协议&#xff08;加密层&#xff09;2. 为什么需要 HTTPS&#xff1f;攻击类型HTTP 风险HTTPS 防护窃听数据明文传输…

作者头像 李华
网站建设 2026/8/12 11:44:06

Cookie 与 Session 的区别及 Cookie 传输机制

1. 核心区别对比表特性CookieSession存储位置客户端浏览器服务器端存储内容文本数据&#xff08;key-value&#xff09;对象数据&#xff08;通常有Session ID&#xff09;安全性较低&#xff08;可被篡改&#xff09;较高&#xff08;服务器控制&#xff09;存储大小每个域名通…

作者头像 李华
网站建设 2026/8/12 13:09:09

称重式雨雪量计:科技赋能,让降水监测更“智”在

称重式雨雪量计是一种基于称重原理的高精度、全天候降水量监测设备&#xff0c;能够准确测量液态、固态及固液混合态降水&#xff0c;广泛应用于气象、水文、农业、交通等多个领域。一、工作原理称重式雨量计基于称重原理技术&#xff0c;利用高精度称重传感器测量“降水增量”…

作者头像 李华
网站建设 2026/8/13 22:27:28

关于发那科机器人视觉补偿报警设置

! 程序名称&#xff1a;VISION_COMP! 功能&#xff1a;读取视觉系统发送的偏移量&#xff0c;并修正目标抓取位姿! 输入&#xff1a;! - GI[11]-GI[14]&#xff1a;视觉系统发送的原始补偿值&#xff08;整数&#xff09;! - PR[249]&#xff1a;预设的理论抓取目标位姿! - R[2…

作者头像 李华
网站建设 2026/8/12 17:27:12

基于单片机的多路温度检测语音报警系统设计

基于单片机的多路温度检测语音报警系统设计 第一章&#xff1a;系统设计目标与需求分析 本系统核心目标是实现多区域温度实时监测与异常语音报警&#xff0c;解决传统单路温控系统覆盖范围有限、报警方式单一的问题&#xff0c;适配工业车间、仓储库房、实验室等多场景温度管…

作者头像 李华