线程间通信

embedded/2025/1/15 21:13:55/

线程间通信(Inter-Thread Communication, 简称ITC)是指在多线程编程中,不同线程之间如何交换信息或协调彼此的行为。良好的线程间通信机制是构建高效、可靠的并发程序的关键。Java语言提供了多种内置工具和库来支持线程间的通信,包括但不限于锁、条件变量、信号量、管道等。

为什么需要线程间通信?

当多个线程共享资源或执行相互依赖的任务时,确保它们能够正确地协作就显得尤为重要。通过适当的线程间通信手段,我们可以实现以下目标:

  • 同步操作:保证某些关键代码段在同一时刻只被一个线程访问。
  • 数据共享:安全地传递数据给其他线程,避免竞态条件(Race Condition)的发生。
  • 任务协调:控制线程的启动顺序、等待时机以及完成状态。
  • 事件通知:让一个线程能够在特定事件发生时唤醒另一个线程。

Java中的线程间通信方式

1. 使用 wait() 和 notify()/notifyAll()

wait()notify()/notifyAll() 是最基础也是最常用的线程间通信方法之一,它们必须在同步上下文中使用(即在 synchronized 块或方法内)。wait() 方法使当前线程进入等待状态,并释放对象锁;而 notify()notifyAll() 则用于唤醒一个或所有正在等待该对象锁的线程。

java">public class BoundedBuffer {private final Object lock = new Object();private List<Integer> buffer = new ArrayList<>();private int capacity;public BoundedBuffer(int capacity) {this.capacity = capacity;}public void put(int item) throws InterruptedException {synchronized (lock) {while (buffer.size() == capacity) {lock.wait(); // 当缓冲区满时,生产者线程等待}buffer.add(item);System.out.println("Put: " + item);lock.notifyAll(); // 唤醒消费者线程}}public int take() throws InterruptedException {synchronized (lock) {while (buffer.isEmpty()) {lock.wait(); // 当缓冲区为空时,消费者线程等待}int item = buffer.remove(0);System.out.println("Take: " + item);lock.notifyAll(); // 唤醒生产者线程return item;}}
}

2. 使用 Lock 接口和 Condition 类

从Java 5开始,java.util.concurrent.locks 包引入了更灵活的锁机制——Lock 接口及其子类,如 ReentrantLock。同时,Condition 类可以看作是 Objectwait/notify 操作的替代品,提供了更加细粒度的线程等待和唤醒功能。

java">import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class BoundedBufferWithLock {private final Lock lock = new ReentrantLock();private final Condition notFull = lock.newCondition();private final Condition notEmpty = lock.newCondition();private final List<Integer> items = new ArrayList<>();private final int capacity;public BoundedBufferWithLock(int capacity) {this.capacity = capacity;}public void put(int item) throws InterruptedException {lock.lock();try {while (items.size() == capacity) {notFull.await(); // 当缓冲区满时,生产者线程等待}items.add(item);System.out.println("Put: " + item);notEmpty.signal(); // 唤醒消费者线程} finally {lock.unlock();}}public int take() throws InterruptedException {lock.lock();try {while (items.isEmpty()) {notEmpty.await(); // 当缓冲区为空时,消费者线程等待}int item = items.remove(0);System.out.println("Take: " + item);notFull.signal(); // 唤醒生产者线程return item;} finally {lock.unlock();}}
}

3. 使用 BlockingQueue 接口

BlockingQueue 是一种特殊的队列,它不仅实现了 Queue 接口的所有功能,而且还提供了阻塞插入和移除元素的方法。这使得它非常适合用来实现生产者-消费者模式下的线程间通信。

java">import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;public class ProducerConsumerExample {private static final int CAPACITY = 10;private static BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(CAPACITY);public static void main(String[] args) {Thread producer = new Thread(() -> {for (int i = 0; i < 20; i++) {try {queue.put(i); // 如果队列已满,则阻塞直到有空间System.out.println("Produced: " + i);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}});Thread consumer = new Thread(() -> {for (int i = 0; i < 20; i++) {try {Integer item = queue.take(); // 如果队列为空,则阻塞直到有元素System.out.println("Consumed: " + item);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}});producer.start();consumer.start();try {producer.join();consumer.join();} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}

4. 使用 CountDownLatchCyclicBarrier 和 Semaphore

这些工具属于同步辅助类,它们为线程间的协调提供了不同的语义:

  • CountDownLatch:允许一个或多个线程等待其他一组线程完成一系列操作后继续执行。
  • CyclicBarrier:让一组线程相互等待,直到所有线程都到达某个公共屏障点再一同继续。
  • Semaphore:控制同时访问某一资源的最大线程数,类似于操作系统中的信号量概念。

5. 使用 Exchanger 类

Exchanger<V> 是一个用于两个线程之间交换数据对象的工具。每个线程调用 exchange(V) 方法,在配对的另一个线程也调用了相同方法之后,两者的数据会被互换。

java">import java.util.concurrent.Exchanger;public class ExchangerExample {private static final Exchanger<String> exchanger = new Exchanger<>();public static void main(String[] args) {Thread threadA = new Thread(() -> {try {String data = "Hello from A";String received = exchanger.exchange(data);System.out.println("Thread A received: " + received);} catch (InterruptedException e) {Thread.currentThread().interrupt();}});Thread threadB = new Thread(() -> {try {String data = "Hello from B";String received = exchanger.exchange(data);System.out.println("Thread B received: " + received);} catch (InterruptedException e) {Thread.currentThread().interrupt();}});threadA.start();threadB.start();try {threadA.join();threadB.join();} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}

线程间通信的最佳实践

  • 最小化同步范围:尽量减少同步块或方法的作用域,只保护真正需要保护的资源。
  • 避免死锁:设计时要特别小心,防止形成循环等待链,即多个线程互相持有对方所需的锁。
  • 使用超时机制:对于可能长时间阻塞的操作,考虑设置合理的超时时间以提高系统的健壮性。
  • 优先选择高级并发工具:相比于原始的 wait/notify,应该更多地利用 java.util.concurrent 包中提供的高级工具,因为它们通常更加安全可靠且易于使用。
  • 文档化通信协议:清晰地记录各个线程之间的通信规则和约定,有助于后续维护人员理解代码逻辑。

结语

感谢您的阅读!如果您对线程间通信或其他并发编程话题有任何疑问或见解,欢迎继续探讨。


http://www.ppmy.cn/embedded/154200.html

相关文章

基于mediapipe的手势游戏控制

基于mediapipe的手势游戏控制 ​ 玩游戏&#xff0c;那不是有手就行!!! mediapipe介绍 ​ Mediapipe是Google在2019年开发并提出的一款开源的跨平台多媒体处理框架&#xff0c;用于构建基于机器学习的应用程序&#xff0c;特别是涉及到计算机视觉、音频处理、姿势估计等领域。…

汽车故障码U007388 PCAN Bus OFF 解析和处理方法

一、故障码解析 含义&#xff1a; U007388 是一个汽车故障码&#xff0c;“U” 开头的故障码一般涉及网络通信故障。PCAN&#xff08;Power Control Area Network&#xff09;通常是指动力控制局域网。“Bus OFF” 表明该网络处于离线状态&#xff0c;意味着 PCAN 网络中的某些…

leetcode 87. 扰乱字符串

题目&#xff1a;87. 扰乱字符串 - 力扣&#xff08;LeetCode&#xff09; dfs状态记录。 dfs&#xff1a;以两个字符串 [a1,a2,a3,a4] 和 [b1,b2,b3,b4]为例&#xff0c;可以往下搜以下几种情况&#xff0c;一种情况为true就能返回true F([a1],[b1]) && F([a2,a3,a4…

Linux 常见运营维护,从安装软件开始,到mysql,php,redis,tomcat等软件安装,配置,优化,持续更新中。。。

下载centos7 CentOS 7 完整版&#xff08;DVD&#xff09;&#xff1a; https://mirrors.aliyun.com/centos/7/isos/x86_64/CentOS-7-x86_64-DVD-2009.isoCentOS 7 最小化版&#xff08;Minimal&#xff09;&#xff1a; https://mirrors.aliyun.com/centos/7/isos/x86_64/C…

[ComfyUI]接入Google的Whisk,巨物融合玩法介绍

一、介紹​ 前段时间&#xff0c;谷歌推出了一个图像生成工具whisk&#xff0c;有一个很好玩的图片融合玩法&#xff0c;分别提供三张图片,就可以任何组合来生成图片。​ ​ 最近我发现有人开发了对应的ComfyUI插件&#xff0c;对whisk做了支持&#xff0c;就来体验了下&#…

微信小程序:实现首页权限菜单效果

一、效果 二、数据库表 1、菜单表 包含权限名称,模块名称,图标名称,页面跳转的方法,菜单是否显示栏位 2、角色对应权限表 包含角色id和权限id,这个表将角色和菜单角色连接,给角色赋予权限功能 3、 账户表 用于绑定账号隶属于什么角色,绑定的角色表 4、角色表 菜单的…

【机器学习】数学知识:指数函数(exp)

在数学和编程中&#xff0c;exp 表示指数函数&#xff0c;即自然常数 e 为底的幂函数。 其数学表达式为&#xff1a; 其中&#xff1a; e 是一个常数&#xff0c;称为自然对数的底数&#xff0c;其值约为 2.718。x 是指数。 性质 基本性质&#xff1a; 与自然对数的关系&…

wireshark排除私接小路由

1.wireshark打开&#xff0c;发现了可疑地址&#xff0c;合法的地址段DHCP是192.168.100.0段的&#xff0c;打开后查看发现可疑地址段&#xff0c;分别是&#xff0c;192.168.0.1 192.168.1.174 192.168.1.1。查找到它对应的MAC地址。 ip.src192.168.1.1 2.通过show fdb p…