【Java 基础篇】Java Condition 接口详解

在这里插入图片描述

Java 提供了一种更灵活和高级的线程协作机制,通过 Condition 接口的使用,你可以更精细地控制线程的等待和唤醒,实现更复杂的线程同步和通信。本文将详细介绍 Java 的 Condition 接口,包括它的基本概念、常见用法以及注意事项。

什么是 Condition 接口?

在 Java 多线程编程中,通常使用 wait()notify() 方法来实现线程之间的等待和唤醒操作。但这两个方法有一些局限性,例如,只能在 synchronized 块内调用,而且每个对象只有一个等待队列。Condition 接口的引入弥补了这些不足,它提供了更灵活的线程协作方式。

Condition 接口是 Java 核心库中 java.util.concurrent.locks 包下的一部分,它通常与 ReentrantLock 一起使用。ReentrantLock 是一种可重入锁,与传统的 synchronized 关键字相比,提供了更多的控制和功能。通过 Condition 接口,你可以为每个 ReentrantLock 创建多个条件(Condition),每个条件可以控制一组线程的等待和唤醒。

Condition 接口的主要方法

Condition 接口定义了一些重要的方法,用于线程的等待和唤醒:

  • await():使当前线程等待,并释放锁,直到其他线程调用相同条件上的 signal()signalAll() 方法来唤醒它。
  • awaitUninterruptibly():与 await() 类似,但不响应中断。
  • signal():唤醒一个在该条件上等待的线程。如果有多个线程在等待,只会唤醒其中一个,具体唤醒哪个线程不确定。
  • signalAll():唤醒所有在该条件上等待的线程。

Condition 的基本用法

创建 Condition

要使用 Condition 接口,首先需要创建一个与 ReentrantLock 关联的条件对象。通常,一个 ReentrantLock 对象可以创建多个条件对象,用于不同的线程协作。

ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();

等待和唤醒线程

在使用 Condition 进行线程协作时,通常遵循以下模式:

等待线程
lock.lock(); // 获取锁
try {while (条件不满足) {condition.await(); // 释放锁,并等待条件满足}// 执行线程任务
} finally {lock.unlock(); // 释放锁
}
唤醒线程
lock.lock(); // 获取锁
try {// 修改条件,使等待线程可以继续执行condition.signal(); // 唤醒一个等待线程// 或者使用 condition.signalAll() 唤醒所有等待线程
} finally {lock.unlock(); // 释放锁
}

示例:生产者和消费者问题

让我们通过一个简单的生产者和消费者问题来演示 Condition 的使用。在这个问题中,有一个有界缓冲区,生产者线程将数据放入缓冲区,而消费者线程将数据从缓冲区取出。

首先,我们创建一个有界缓冲区的类:

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class BoundedBuffer<T> {private Queue<T> buffer = new LinkedList<>();private int capacity;private Lock lock = new ReentrantLock();private Condition notFull = lock.newCondition();private Condition notEmpty = lock.newCondition();public BoundedBuffer(int capacity) {this.capacity = capacity;}public void put(T item) throws InterruptedException {lock.lock();try {while (buffer.size() == capacity) {notFull.await();}buffer.offer(item);notEmpty.signal();} finally {lock.unlock();}}public T take() throws InterruptedException {lock.lock();try {while (buffer.isEmpty()) {notEmpty.await();}T item = buffer.poll();notFull.signal();return item;} finally {lock.unlock();}}
}

在这个示例中,我们使用了 ReentrantLock 来保护缓冲区的操作,并分别创建了两个条件 notFullnotEmpty,用于控制缓冲区的状态。

接下来,我们可以创建生产者和消费者线程,它们分别向缓冲区放入数据和取出数据:

public class ProducerConsumerExample {public static void main(String[] args) {BoundedBuffer<Integer> buffer = new BoundedBuffer<>(10);Thread producerThread = new Thread(() -> {try {for (int i = 0; i < 100; i++) {buffer.put(i);System.out.println("Produced: " + i);}} catch (InterruptedException e) {Thread.currentThread().interrupt();}});Thread consumerThread = new Thread(() -> {try {for (int i = 0; i < 100; i++) {int item = buffer.take();System.out.println("Consumed: " + item);}} catch (InterruptedException e) {Thread.currentThread().interrupt();}});producerThread.start();consumerThread.start();}
}

在这个示例中,生产者线程不断地向缓冲区放入数据,而消费者线程不断地从缓冲区取出数据,它们通过 await()signal() 方法进行线程协作。

注意事项

在使用 Condition 接口时,需要注意以下几点:

  1. 必须在获取锁之后才能调用 await()signal()signalAll() 方法,否则会抛出 IllegalMonitorStateException 异常。

  2. 调用 await() 方法后,当前线程将释放锁,允许其他线程获取锁并执行。当线程被唤醒后,它将重新尝试获取锁,然后从 await() 方法返回。

  3. signal() 方法只能唤醒一个等待线程,如果有多个线程在等待,具体唤醒哪一个是不确定的。如果需要唤醒所有等待线程,可以使用 signalAll() 方法。

  4. 在等待时,通常需要将 await() 方法包装在一个循环中,以防止虚假唤醒。

  5. 使用 Condition 接口时,要特别小心死锁和竞态条件等多线程问题,确保线程协作的正确性和安全性。

总结

Condition 接口提供了一种更灵活和高级的线程协作机制,可以用于实现复杂的线程同步和通信。通过创建多个条件对象,你可以更精细地控制线程的等待和唤醒。但在使用时需要小心处理锁和条件的关系,以确保线程协作的正确性和可靠性。希望本文对你理解和应用 Condition 接口有所帮助,提高多线程编程的技能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/137523.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MyBatis 篇

目录 1、什么是MyBatis 2、说说MyBatis的优点和缺点 3、#{}和${}的区别是什么&#xff1f; 4、当实体类中的属性名和表中的字段名不一样 &#xff0c;怎么办 &#xff1f; 5、Mybatis是如何进行分页的&#xff1f;分页插件的原理是什么&#xff1f; 6、Mybatis是否支…

【大数据之Kafka】十五、Kafka-Kraft模式

1 Kafka-Kraft架构 左图为 Kafka 现有架构&#xff0c;元数据在 zookeeper 中&#xff0c;运行时动态选举 controller&#xff0c;由controller 进行 Kafka 集群管理。   右图为 kraft 模式架构&#xff08;实验性&#xff09;&#xff0c;不再依赖 zookeeper 集群&#xff0…

mysql 锁总结

redo log 是事务持久性的保证 undo log 是事务原子性的保证 锁机制 是事务隔离性的保证 MySQL的自动加锁依赖于数据库的事务机制。当事务对某个数据行进行更新操作时&#xff0c;MySQL就会自动给该行加上排他锁&#xff0c;防止其他事务同时对其进行修改。同时&#xff0c;MySQ…

【Linux】常用工具(下)

Linux常用工具 一、Linux 项目自动化构建工具 - make/Makefile1. 依赖关系和依赖方法2. 伪目标3. make/Makefile 具有依赖性的推导能力&#xff08;语法扩展&#xff09;4. 编写一个进度条代码&#xff08;1&#xff09;缓冲区&#xff08;2&#xff09;\n 和 \r&#xff08;3&…

硬件学习 PAD9.5 day02 原理图绘制中的操作,PCB 封装的制作

1. 原理图中的连线操作 1.1 点击连线按钮 1.2 点击需要连线的地方连接即可 1.3 双击即可停止连线 2. 原理图的总线绘制 2.1 按下总线绘制按钮 2.2 画一条总线 总线名称 总线名字 [ 起始数字 &#xff1a; 结束数字 ] 2.3 分线连接总线 注意&#xff1a;原理图的连线…

MySQL常用配置详解

目录 一、MySQL 查看配置信息二、MySQL 查看服务器当前运行状态的信息三、MySQL 常用配置详解1、mysql&#xff08;使用mysql命令登录数据库时的默认的设置&#xff09;2、client &#xff08;客户端默认设置内容&#xff09;3、mysqld&#xff08;服务端端配置&#xff09; 四…

【计算机网络】 拥塞控制

文章目录 背景TCP的四种拥塞控制算法慢开始与拥塞避免&#xff1a;快重传&#xff1a;快恢复&#xff1a; 流量控制和拥塞控制本质上的 区别 背景 网络中的链路容量和交换节点中的缓存和处理机都有着工作的极限&#xff0c;当网络的需求超过他们的工作极限时&#xff0c;就出现…

黄金代理前景如何,有得搞吗?

现货黄金代理这个职业时常都听人说过了&#xff0c;随着近期现货黄金走势不断出现行情&#xff0c;尤其是美国通胀严重&#xff0c;地缘政治局势频发&#xff0c;黄金走势不断获得支撑而走高。在这样的背景下&#xff0c;现货黄金代理这个职业的前景如何呢&#xff1f; 其实对于…

肖sir__mysql中数据库后端无法展示

mysql中数据库后端无法展示&#xff1a; 错误现象 解决方法&#xff1a; mysql中数据库后端无法展示&#xff1a;my.cnf (5,7数据库) 在 mysql 配置文件中加入&#xff1a; sql_modeNO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES 或者重启数据库

【数据结构】二叉树的节点数,叶子数,第K层节点数,高度,查找x节点,判断是否为完全二叉树等方法

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

开源媒体浏览器Kyoo

什么是 Kyoo &#xff1f; Kyoo 是一款开源媒体浏览器&#xff0c;可让您流式传输电影、电视节目或动漫。它是 Plex、Emby 或 Jellyfin 的替代品。Kyoo 是从头开始创建的&#xff0c;它不是一个分叉。一切都将永远是免费和开源的。 软件特性&#xff1a; 管理您的电影、电视剧…

极致鸿蒙2.0——华为MatePad系列安装AidLux,一个自带vscode的Python编译环境

看着刚刚人入手的华为鸿蒙系统MatePad11平板&#xff0c;是如此的美轮美奂&#xff0c;但是总感觉少了点什么&#xff0c;少了点什么呢&#xff1f;是编程环境&#xff0c;我爱MatePad&#xff0c;也爱编程&#xff0c;那如果可以在MatePad上编程&#xff0c;会发生什么&#x…

性能测试 —— Tomcat监控与调优:status页监控

Tomcat服务器是一个免费的开放源代码的Web 应用服务器&#xff0c;Tomcat是Apache 软件基金会(Apache Software Foundation)Jakarta 项目中的一个核心项目&#xff0c;由Apache、Sun 和其他一些公司及个人共同开发而成。 Tomcat是一个轻量级应用服务器&#xff0c;在中小型系统…

车载通信架构 —— SOME/IP 协议概述

车载通信架构 —— SOME/IP 协议概述 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 对学习而言,学习之后的思考、思考之后的行动、行动之后的改变更重要,如果不盯住内层的改变量,那么在表层投…

Java下打印直角三角型(另一个方向)

代码如下&#xff1a; public class MyWork {public static void main(String[] args) {int num 5;for (int i 0; i < num; i) {for (int j 0; j < i; j) {System.out.print("-");}for (int j 0; j < num - i; j) {System.out.print("*");}S…

OceanBase杨传辉传递亚运火炬:国产数据库为“智能亚运”提供稳稳支持

9 月 14 日&#xff0c;亚运火炬传递到了浙江台州&#xff0c;OceanBase 的 CTO 杨传辉作为火炬手交接了第 89 棒火炬。 2010 年&#xff0c;杨传辉作为创始成员之一参与自研原生分布式数据库 OceanBase。十年磨一剑&#xff0c;国产数据库 OceanBase 交出了一张优秀的成绩单&a…

实施主品牌进化战略(一):确立主品牌进化架构

主品牌进化战略&#xff0c;即以主品牌为核心创造、巩固、转化竞争优势应对竞争环境变化&#xff0c;避免衰退&#xff0c;回归增长&#xff0c;让主品牌进化的方法论体系。主品牌进化战略制定要从 4 个方面出发&#xff1a;确立主品牌进化架构、更新和明确主品牌竞争方向、建立…

【100天精通Python】Day67:Python可视化_Matplotlib 3D绘图,绘制3D曲面图、3D填充图,3D极坐标图,示例+代码

目录 1 绘制曲面图 2 绘制3D填充图 3 绘制极坐标图 1 绘制曲面图 当绘制3D曲面图时&#xff0c;mpl_toolkits.mplot3d 模块中的 Axes3D 对象提供了多种方法来呈现不同类型的曲面图。以下是一些常见的3D曲面图类型以及示例&#xff1a; 曲面图&#xff1a;使用 plot_surface …

修炼离线:(三)sqoop插入hbase 报错权限问题

一&#xff1a;报错现象。 二&#xff1a;解决方式。 方法一&#xff1a;修改文件所有者。 切换hadoop用户&#xff1a;export HADOOP_USER_NAMEhdfs hadoop fs -chown -R root:root /方法二&#xff1a;修改权限 切换hadoop用户&#xff1a;export HADOOP_USER_NAMEhdfs ha…

Proteus的编译运行(以AT89C51为例)

最近&#xff0c;突然又用到了Proteus,之前还是大三上的时候上微机原理的时候用到过&#xff0c;今天记录一下如何在Proteus中编写代码&#xff0c;编译运行。 首先&#xff0c;选中AT89C51芯片&#xff0c;右键选择编辑源代码。 选择芯片的系列与对应的编译器&#xff0c;这里…