当前位置: 首页 > news >正文

Java线程通信

第一部分:为什么要进行线程通信?

线程通信的根本原因是:为了让多个线程能够有效、安全地协同工作,以完成一个共同的任务。

当程序从单线程变成多线程时,会引入两个核心问题:

  1. 竞争条件: 多个线程同时读写共享资源(如一个变量、一个数据结构、一个文件等),导致最终结果依赖于线程执行的精确时序,从而产生不可预测、错误的结果。

    • 经典例子: 两个线程同时对一个银行账户余额进行“+1”操作。理想结果是余额+2,但由于读写交叉执行,最终可能只+1。

  2. 内存可见性: 由于现代CPU的多级缓存架构和编译器的优化(如指令重排序),一个线程对共享资源的修改可能不会立即被其他线程看到,导致其他线程使用过期的数据。

因此,线程通信的目的就是为了解决这两个问题,具体来说:

  • 同步(Synchronization): 控制多个线程对共享资源的访问顺序,确保在任一时刻只有一个(或特定数量的)线程可以访问临界资源,从而避免竞争条件。这是线程通信的“安全”目的。

  • 协作(Cooperation): 让线程之间能够彼此通知和等待,以便协调他们的工作步调。例如,生产者线程生产了数据后通知消费者线程来消费;一个线程需要等待其他多个线程都完成任务后才能继续。这是线程通信的“协同”目的。

没有有效的线程通信机制,多线程程序就会陷入混乱,结果不可预测,也就是我们常说的“线程不安全”。


第二部分:多线程通信的方式

线程通信的方式多种多样,可以根据不同的抽象层级和场景来划分。以下是主流的几种方式:

1. 同步/锁机制

使用同步、保护多个线程共享访问的核心工具

// 共享计数器,使用synchronized保证线程安全
class SharedCounter {private int count = 0;public synchronized void increment() {count++;System.out.println(Thread.currentThread().getName() + ": " + count);}
}public class SharedMemoryExample {public static void main(String[] args) {SharedCounter counter = new SharedCounter();// 两个线程共享同一个计数器new Thread(() -> {for(int i = 0; i < 5; i++) counter.increment();}).start();new Thread(() -> {for(int i = 0; i < 5; i++) counter.increment();}).start();}
}

2. 等待/通知机制

这是线程间协作的核心机制,Java 中每个对象都内置了 wait()notify()notifyAll() 方法,但它们必须在 synchronized 同步块内使用。

// 简单的生产者-消费者模型
class Message {private String content;private boolean empty = true;public synchronized String read() {while(empty) {try {wait(); // 等待消息} catch (InterruptedException e) {}}empty = true;notifyAll(); // 通知生产者return content;}public synchronized void write(String message) {while(!empty) {try {wait(); // 等待消费} catch (InterruptedException e) {}}empty = false;this.content = message;notifyAll(); // 通知消费者}
}public class WaitNotifyExample {public static void main(String[] args) {Message message = new Message();// 消费者线程new Thread(() -> {String result;while((result = message.read()) != null) {System.out.println("收到: " + result);}}).start();// 生产者线程new Thread(() -> {String[] messages = {"你好", "世界", "结束"};for(String msg : messages) {message.write(msg);System.out.println("发送: " + msg);}}).start();}
}

3. volatile 关键字

保证了变量的可见性(一个线程修改后,新值立即对其他线程可见)和禁止指令重排序。它不能保证操作的原子性(例如 volatile int i; i++ 这个操作不是原子的)。它通常用作一个简单的状态标志位。

// 使用volatile保证可见性
class VolatileFlag {private volatile boolean flag = false;public void setFlag() {flag = true;System.out.println("标志已设置");}public void checkFlag() {while(!flag) {// 空循环,等待flag变为true}System.out.println("检测到标志变化");}
}public class VolatileExample {public static void main(String[] args) throws InterruptedException {VolatileFlag example = new VolatileFlag();// 检测线程new Thread(() -> example.checkFlag()).start();Thread.sleep(1000); // 等待1秒// 设置线程new Thread(() -> example.setFlag()).start();}
}

4. 信号量

它维护一个许可证数量。线程通过 acquire() 获取许可证,如果数量为0则阻塞;通过 release() 释放许可证。它可以控制同时访问某个资源的线程数量。

// 使用信号量控制资源访问
class ResourcePool {private Semaphore semaphore;public ResourcePool(int limit) {semaphore = new Semaphore(limit);}public void useResource() {try {semaphore.acquire(); // 获取许可System.out.println(Thread.currentThread().getName() + " 使用资源");Thread.sleep(1000); // 模拟资源使用System.out.println(Thread.currentThread().getName() + " 释放资源");semaphore.release(); // 释放许可} catch (InterruptedException e) {e.printStackTrace();}}
}public class SemaphoreExample {public static void main(String[] args) {ResourcePool pool = new ResourcePool(2); // 最多2个线程同时访问// 创建5个线程尝试访问资源for(int i = 0; i < 5; i++) {new Thread(() -> pool.useResource(), "线程-" + i).start();}}
}

5. 管道流

用创建一个管道,一端是写入线程,另一端是读取线程。写入线程向管道输出流写数据,读取线程从管道输入流读数据。Java 提供了 PipedInputStream 和 PipedOutputStream

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;// 使用管道在线程间传递数据
public class PipeExample {public static void main(String[] args) throws IOException {final PipedOutputStream output = new PipedOutputStream();final PipedInputStream input = new PipedInputStream(output);// 写入线程Thread writer = new Thread(() -> {try {output.write("Hello from pipe!".getBytes());output.close();} catch (IOException e) {e.printStackTrace();}});// 读取线程Thread reader = new Thread(() -> {try {int data;while((data = input.read()) != -1) {System.out.print((char) data);}input.close();} catch (IOException e) {e.printStackTrace();}});writer.start();reader.start();}
}

6. 高级并发工具类

现代编程语言(尤其是Java)提供了大量高级并发工具,简化了线程通信。

  • CountDownLatch: 一个或多个线程等待其他一组线程完成操作。例如,主线程等待所有工作线程初始化完毕。

  • CyclicBarrier: 让一组线程互相等待,直到所有线程都到达一个屏障点再继续执行。类似于“等人齐了再开会”。

  • Exchanger: 用于两个线程之间交换数据。在一个交换点上,两个线程会彼此等待,然后交换各自的数据。

  • 生产者-消费者:用 BlockingQueue 通信,自动“有数据就通知消费,没数据就等待”,多用于替代“管道流”。


    文章转载自:

    http://6nTXQGFR.mfsxd.cn
    http://aku4RBBz.mfsxd.cn
    http://6TAlBPj1.mfsxd.cn
    http://xdDnI3x5.mfsxd.cn
    http://zPo2VhdK.mfsxd.cn
    http://SI60iFeC.mfsxd.cn
    http://i8wLLsHP.mfsxd.cn
    http://EMDWZGSt.mfsxd.cn
    http://huNNoN9k.mfsxd.cn
    http://VxdpI9eb.mfsxd.cn
    http://kwqeygh0.mfsxd.cn
    http://x05ubmqi.mfsxd.cn
    http://AuFrjSpz.mfsxd.cn
    http://w6wg66o7.mfsxd.cn
    http://PLshhj4e.mfsxd.cn
    http://lfzzmrOD.mfsxd.cn
    http://ioZdCneN.mfsxd.cn
    http://NubcGOLi.mfsxd.cn
    http://PLQflbb8.mfsxd.cn
    http://TfNTLXMT.mfsxd.cn
    http://1xkQiSdw.mfsxd.cn
    http://DJGcPGW1.mfsxd.cn
    http://XQcixVsB.mfsxd.cn
    http://n7UC8wSu.mfsxd.cn
    http://qhHKYovr.mfsxd.cn
    http://DbH7su6l.mfsxd.cn
    http://iwBYltM9.mfsxd.cn
    http://xCiyk4f6.mfsxd.cn
    http://TEQSlioT.mfsxd.cn
    http://CUQAj6vo.mfsxd.cn
    http://www.dtcms.com/a/369874.html

    相关文章:

  • Linux匿名管道和命名管道以及共享内存
  • 【01背包问题变体】P1282 多米诺骨牌
  • 【C++】类和对象(三)
  • 【面试向】人工智能机器学习介绍
  • 轻量级XML读写库Mini-XML的编译和使用
  • 【数论】P10580 [蓝桥杯 2024 国 A] gcd 与 lcm|普及+
  • 微服务的编程测评系统22-项目部署结束
  • DNS解析中的服务器协作机制
  • Windows权限提升(二)
  • Homebrew执行brew install出现错误(homebrew-bottles)
  • 数据结构之二叉树(1)
  • Vue 3项目中引用ECharts并设计多种图表组件的实现方案
  • Day37 MQTT协议 多客户端服务器模型
  • 【UAV】基于PX4+Ubuntu24.04.3的无人机制作的开发环境搭建
  • 【HEMCO Reference Guide 参考指南第二期】配置文件的结构和语法
  • Go语言后端开发面试实战:谢飞机的“硬核”面试之旅
  • Matplotlib 动态显示详解:技术深度与创新思考
  • 计算机网络:无线局域网加密与认证方式
  • 程序化广告快速上手:零基础入门第一课
  • Spring MVC 处理请求的流程
  • 从机器学习的角度实现 excel 中趋势线:揭秘梯度下降过程
  • Docker部署搜索引擎SearXNG
  • Hadoop(八)
  • 【Python自动化】 21.2 Pandas 读取 Excel 时的 dtype 参数完全指南
  • 【面板数据】各省制造业出口技术复杂度数据集(2010-2023年)
  • 使用 YAML 自动化 Azure DevOps 管道
  • 【数据库相关】TxSQL新增数据库节点步骤
  • 理想汽车智驾方案介绍 4 World model + 强化学习重建自动驾驶交互环境
  • 大语言模型预训练数据采集与清洗技术实践:从语料到知识库的全流程优化
  • 腾讯混元翻译模型Hunyuan-MT-7B开源,先前拿了30个冠军