深入浅出Java Condition 的await和signal机制(一)

深入浅出Java Condition 的await和signal机制(一)

每个对象都可以调用 Object 的 wait/notify 方法来实现等待/通知机制。而 Condition 接口也提供了类似的方法。Condition 接口一共提供了以下 7 个方法:

  • await():线程等待直到被通知或者中断。类似于 Object.wait()。
  • awaitUninterruptibly():线程等待直到被通知,即使在等待时被中断也不会返回。没有与之对应的 Object 方法。
  • await(long time, TimeUnit unit):线程等待指定的时间,或被通知,或被中断。类似于 Object.wait(long timeout),但提供了更灵活的时间单位。
  • awaitNanos(long nanosTimeout):线程等待指定的纳秒时间,或被通知,或被中断。没有与之对应的 Object 方法。
  • awaitUntil(Date deadline):线程等待直到指定的截止日期,或被通知,或被中断。没有与之对应的 Object 方法。
  • signal():唤醒一个等待的线程。类似于 Object.notify()。
  • signalAll():唤醒所有等待的线程。类似于 Object.notifyAll()。

以下是Object 类的主要方法,我们来做一下对比:

  • wait():线程等待直到被通知或者中断。
  • wait(long timeout):线程等待指定的时间,或被通知,或被中断。
  • wait(long timeout, int nanos):线程等待指定的时间,或被通知,或被中断。
  • notify():唤醒一个等待的线程。
  • notifyAll():唤醒所有等待的线程。

Condition 源码分析

要想深入理解 Condition 的实现原理,就需要挖掘一下 Condiiton 的源码。创建一个 Condition 对象可以通过lock.newCondition() 来创建,这个方法实际上会 new 一个 ConditionObject 的对象,ConditionObject 是 AQS 的一个内部类,我们就拿 ReentrantLock 来举例说明吧。

public class ReentrantLock implements Lock, java.io.Serializable { abstract static class Sync extends AbstractQueuedSynchronizer { final ConditionObject newCondition() { return new ConditionObject(); } } public Condition newCondition() { return sync.newCondition(); } }

AQS 内部维护了一个先进先出(FIFO)的双端队列,并使用了两个引用 head 和 tail 用于标识队列的头部和尾部。

Condition 内部也使用了同样的方式,内部维护了一个先进先出(FIFO)的单向队列,我们把它称为等待队列。

所有调用 await 方法的线程都会加入到等待队列中,并且线程状态均为等待状态。firstWaiter 指向首节点,lastWaiter 指向尾节点,源码如下:

public class ConditionObject implements Condition, java.io.Serializable { private static final long serialVersionUID = 1173984872572414699L; /** First node of condition queue. */ private transient Node firstWaiter; /** Last node of condition queue. */ private transient Node lastWaiter; }

Node 中的 nextWaiter 指向队列中的下一个节点。并且进入到等待队列的 Node 节点状态都会被设置为 CONDITION。同时还有一点需要注意:我们可以多次调用newCondition()方法创建多个 Condition 对象,也就是一个 lock 可以持有多个等待队列。而如果是 Object 方式的话,就只能有一个同步队列和一个等待队列。因此,ReentrantLock 等 AQS 是可以持有一个同步队列和多个等待队列的,new 多个 Condition 就行了。示意图如下:AQS持有多个Condition

持有多个等待队列的好处是什么呢?我们可以通过下面这个例子来说明:

public class BoundedBuffer<T> { private final LinkedList<T> buffer; // 使用 LinkedList 作为缓冲区 private final int capacity; // 缓冲区最大容量 private final ReentrantLock lock; // 互斥锁 private final Condition notEmpty; // 缓冲区非空条件 private final Condition notFull; // 缓冲区非满条件 public BoundedBuffer(int capacity) { this.capacity = capacity; this.buffer = new LinkedList<>(); this.lock = new ReentrantLock(); this.notEmpty = lock.newCondition(); this.notFull = lock.newCondition(); } // 放入一个元素 public void put(T item) throws InterruptedException { lock.lock(); try { // 如果缓冲区满,等待 while (buffer.size() == capacity) { notFull.await(); } buffer.add(item); // 通知可能正在等待的消费者 notEmpty.signal(); } finally { lock.unlock(); } } // 取出一个元素 public T take() throws InterruptedException { lock.lock(); try { // 如果缓冲区空,等待 while (buffer.isEmpty()) { notEmpty.await(); } T item = buffer.removeFirst(); // 通知可能正在等待的生产者 notFull.signal(); return item; } finally { lock.unlock(); } } }

考虑这个简单的有界缓冲区 BoundedBuffer,其中生产者放入元素,消费者取出元素。我们将使用两个 Condition:一个表示缓冲区不为空(用于消费者等待),另一个表示缓冲区不满(用于生产者等待)。生产者调用 put 方法放入元素,如果缓冲区已满,则等待 notFull 条件。消费者调用 take 方法取出元素,如果缓冲区为空,则等待 notEmpty 条件。当一个元素被放入或取出时,相应的条件会发出信号,唤醒等待的线程。使用多个 Condition 对象的主要优点是为锁提供了更细粒度的控制,可以实现更复杂的同步场景,比如上面提到的有界缓冲区。

Read more

Java初识面向对象+类与对象+封装核心

Java初识面向对象+类与对象+封装核心

🏠个人主页:黎雁 🎬作者简介:C/C++/JAVA后端开发学习者 ❄️个人专栏:C语言、数据结构(C语言)、EasyX、JAVA、游戏、规划、程序人生 ✨ 从来绝巘须孤往,万里同尘即玉京 文章目录 * ✨Java面向对象精讲(一):初识面向对象+类与对象+封装核心|零基础吃透OOP思想 * 📌 文章摘要(248字) * 🕒 阅读时长:约12分钟 * ✅ 适用人群 & 阅读重点 * 📖 知识回顾(课前必看,快速衔接) * 一、初识面向对象 ☀️ 从生活到代码,彻底理解核心思想 * 1.1 什么是面向对象?(生活案例,通俗易懂) * 1.2 我们要学的两大核心内容 * 二、设计对象并使用 ✍️ 类与对象【核心重点,

By Ne0inhk
将openclaw接入飞书:10分钟,让你的AI员工直接操作你的文档和表格!

将openclaw接入飞书:10分钟,让你的AI员工直接操作你的文档和表格!

上一篇,我们给小龙虾接了 Telegram,实现了手机遥控。 但说实话,Telegram 只解决了"能聊天"的问题。你跟小龙虾说"帮我写个文档",它写完了——然后呢?你还得自己复制粘贴到你的编辑器中。 这就像请了个助手,他只能站在门外隔着门跟你喊话,但不能进屋帮你干活。 今天这篇,我们把门打开。让小龙虾直接进入你的飞书——读文档、写文档、操作表格、管理日程,全部自己来。 先看效果👇 飞书的配置比 Telegram 多一些步骤,但别慌——跟着我走,每一步都有截图,大概10分钟搞定。 飞书的接入分四个阶段,先有个全局概念,不容易迷路: 1. 在飞书上造一个机器人 — 相当于给小龙虾办一张飞书工牌 2. 在服务器上装飞书插件 — 让小龙虾学会"说飞书的语言" 3.

By Ne0inhk
【Java 开发日记】阻塞队列有哪些?拒绝策略有哪些?

【Java 开发日记】阻塞队列有哪些?拒绝策略有哪些?

目录 阻塞队列有哪些? 拒绝策略有哪些? 面试回答 阻塞队列有哪些? 在Java的java.util.concurrent包里面,阻塞队列的实现挺多的,我们可以根据它的功能和结构来记,主要分这么几类: 1. 按容量划分: * 有界队列: 就是队列有固定的容量。 * ArrayBlockingQueue: 最经典的一个,底层是数组,创建时必须指定大小。它的生产和消费用同一把锁,性能相对稳定。 * LinkedBlockingQueue: 底层是链表,它既可以是有界的(构造时指定容量),也可以默认是无界的(默认是Integer.MAX_VALUE,几乎相当于无界)。它的生产和消费用了两把锁,在高并发场景下吞吐量通常比ArrayBlockingQueue更高。 * 无界队列: 理论上是无限的,只要内存够就能一直放。 * PriorityBlockingQueue: 一个支持优先级排序的无界队列。元素必须实现Comparable接口,或者构造时传入Comparator。它出队的顺序是按优先级来的,不是先进先出 * DelayQueue: 一个很特殊的队

By Ne0inhk
Java 大视界 -- 基于 Java 的大数据实时流处理在工业物联网设备状态监测中的应用与挑战

Java 大视界 -- 基于 Java 的大数据实时流处理在工业物联网设备状态监测中的应用与挑战

Java 大视界 -- 基于 Java 的大数据实时流处理在工业物联网设备状态监测中的应用与挑战 * 引言 * 正文 * 一、工业物联网设备状态监测概述 * 二、基于 Java 的大数据实时流处理技术 * 2.1 技术架构与原理 * 2.2 状态管理与故障恢复 * 三、应用案例分析 * 四、引入边缘计算优化架构 * 五、面临的挑战与应对策略 * 5.1 数据质量问题 * 5.2 系统性能瓶颈 * 5.3 安全与隐私保护 * 结束语 * 🗳️参与投票和联系我: 引言 亲爱的 Java 和 大数据爱好者们,大家好!在科技引领产业变革的时代,大数据技术已成为推动各行业智能化转型的核心引擎。此前,我们通过一系列文章,深入探讨了 Java 大数据在金融、

By Ne0inhk