深入浅出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

Windows下MATLAB与C/C++混合编程:DLL生成与调用实战

Windows下MATLAB与C/C++混合编程:DLL生成与调用实战

Windows下MATLAB与C/C++混合编程:DLL生成与调用实战 在科学计算与工程开发中,MATLAB凭借其便捷的矩阵运算和可视化能力广受青睐,但面对大规模数据处理或高性能算法时,C/C++的执行效率优势无可替代。将二者结合,通过动态链接库(DLL) 实现混合编程,既能发挥MATLAB的易用性,又能借助C/C++提升核心代码性能。本文将手把手教你在Windows环境下完成从C/C++ DLL编写、编译到MATLAB调用的全流程,附带完整代码与避坑指南! 一、核心原理与准备工作 1. 核心逻辑 C/C++编译生成的DLL文件包含可被外部程序调用的函数,通过__declspec(dllexport)声明导出函数,并使用extern "C"指定C链接规范,避免C++的名称修饰(name mangling)问题,确保MATLAB能正确识别函数名。 MATLAB通过loadlibrary函数加载DLL,解析函数接口后,使用calllib函数调用目标函数,实现数据交互。 2.

By Ne0inhk

基于Java Web的城市花园小区维修管理系统的设计与实现(源码+论文+部署+安装)

感兴趣的可以先收藏起来,还有在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,我会一一回复,希望可以帮到大家。 一、程序背景 在城市化高速发展背景下,城市园林小区规模和数量不断增加,维修管理作为小区物业管理的核心环节,直接关系到住户生活品质,但传统维修管理模式依赖纸质记录、电话沟通和手工巡检,存在信息传递不及时、维护响应缓慢、过程难以追溯、数据统计不精准等问题,既增加了物业管理成本,也降低了业主满意度。同时,随着互联网技术的普及,业主对信息化、智能化的物业服务需求日益提升,希望通过便捷的线上平台实现报修、查进度、反馈意见等操作。为此,基于 Java 网络技术,开发城市花园小区维修管理系统,解决传统管理痛点,推动小区维修管理信息化、智能化升级,满足现代化住宅小区管理需求。 二、程序功能需求 系统围绕管理员、业主(用户)、维修工三大角色设计,覆盖 “报修 - 派单 - 维修 - 反馈 -

By Ne0inhk

【Java基础面试题】Java特点,八种基本数据类型

Java的特点 * 1.面向对象 * 2.跨平台 * 3.简单易学 * 4.内存管理 JVM、JDK、JRE的关系 * JVM:Java虚拟机,它负责把字节码文件进行编译并运行,是Java具有跨平台性的关键所在。它还有内存管理,垃圾回收等功能。 * JDK:JDK是Java开发工具包,它包含了JVM,编译器,调试器等开发工具,还包含一系列类库。它提供了Java程序编译,调试,运行所需要的工具和环境。 * JRE:是java运行时的最小环境,它包含JVM和一组Java类库,用于支持java运行。 数据类型 八种基本数据类型: 整型:byte,short,int,long 浮点型:float,double 字符型:char 布尔类型:boolen 这八种基本类型都有对应的包装类分别为:Byte、Short、Integer、Long、

By Ne0inhk
从 .NET 到 Java 的转型指南:详细学习路线与实践建议

从 .NET 到 Java 的转型指南:详细学习路线与实践建议

文章目录 * 第一部分:转型背景与核心差异分析 * 1.1 为什么需要从 .NET 转型到 Java * 1.2 .NET 与 Java 核心架构差异 * 1.2.1 运行时环境对比 * 1.2.2 内存管理机制 * 1.3 心态调整与学习策略 * 1.3.1 相似性利用 * 1.3.2 差异性重视 * 第二部分:Java 语言基础深入学习 * 2.1 Java 语法核心概念 * 2.1.1 基本数据类型与包装类 * 2.1.2 字符串处理 * 2.

By Ne0inhk