Java线程通信

Java多线程之间的通信 在 Java 多线程编程中,​​线程通信​​是指多个线程之间通过某种机制进行数据交换或协调执行顺序,以确保线程间能够正确、高效地协作完成任务。由于线程是并发执行的独立个体,默认情况下它们之间是相互隔离的,但实际业务中常常需要线程间共享数据或同步操作(如生产者-消费者模型),这时就需要借助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 通信,自动“有数据就通知消费,没数据就等待”,多用于替代“管道流”。

    CATIA P3 V5-6R2023在Windows 11 23H2上的稳定安装与系统级配置指南 CATIA是高端制造业核心的三维参数化设计平台,其运行依赖严格的系统底层支撑。理解CATIA的BRep几何内核原理与Windows子系统(如WSL、Hyper-V)的协同机制,是保障建模稳定性与计算精度的技术前提。CATIA Run-Time Library和OpenGL硬件加速能力共同决定了曲面重建、螺纹建模等关键操作的可靠性;而.NET Framework 3.5、VC++ 2022运行库及显卡Studio驱动的精准匹配,则构成工业软件在Win11环境落地的工程基础。本文聚焦于航空、轨交、能源等领域高频 阅读详情

    相关推荐

    告别绕线!用Altium Designer从零设计一个PCB平面变压器(附3D模型)

    本文详细介绍了如何使用Altium Designer从零设计一个PCB平面变压器,包括磁芯选型、电气参数计算、PCB设计实战技巧以及3D验证与生产输出。通过将绕组直接集成到PCB层中,配合嵌入式磁芯,实现了小型化、高功率密度的电源设计,适用于中低电流、高频应用场景。附3D模型,帮助读者快速掌握这一创新设计流程。

    weixin_33716154的博客 442

    Java线程通信的 5 个常见问题

    多个线程协同工作完成某个任务时就会涉及到线程间通信问题。如何使各个线程之间同时执行,顺序执行、交叉执行等。

    2401_83384536的博客 388

    从 Pandas 到 Polars 十八:数据科学 2025,对未来几年内数据科学领域发展的预测或展望

    Rust 在数据科学家中的普及将受到 Polars 的推动,用户会发现从使用 Python 切换到 Rust API 的门槛相对较低——比从 Pandas 切换到 C 的门槛要低得多!在数据科学蓬勃发展的十年里,我所在领域的日常工作主要由像 Python 这样的动态语言或像 R 和 Julia 这样使用即时编译的语言主导。尽管这两个库的功能有很多重叠,但有些人会想要一个像 DuckDB 这样的工具,它拥有更多关系型数据库的陷阱,而有些人则不会。然而,一位受访者正确地指出,基于GPU的库(如cuDF)更快。

    sosogod的博客 2467

    关于线程通信问题(面试题)

    关于volitale,notify,wait面试问题:   1、问题:两个线程操作一个对象时,使得其中的一个线程不要一直死循环等待另外一个线程       解决思路:上述情况如果不添加volatile关键字就会是一个线程一直循环等待    (1)代码示例: package net.oschina.tkj.mulitcoding.notifykey; import java.ut...

    u013777494的博客 858

    版图提取的spice网表中参数含义及二级效应STI/WPE

    寄生电阻、寄生电容可以从版图提出的.sp文件中看, ad area of drain 面积 as area of source pd perimeter of drain 周长,PS PD周长都不计算与栅极接触的部分 ps perimeter of source nrd 漏扩散区等效方块数 nrs 源扩散区等效方块数 sa ...

    qq_40007892的博客 6552

    Stata实战:5步搞定PSM倾向得分匹配(附完整代码与常见报错解决)

    本文详细介绍了使用Stata进行PSM(倾向得分匹配)的5步实战流程,包括数据预处理、模型构建、匹配算法选择、匹配质量诊断和结果解释。提供完整代码示例和常见报错解决方案,帮助研究者高效完成实证分析,提升匹配成功率。

    weixin_29214559的博客 590

    Java 线程间通信

    Java线程间通信

    WYSCODER的博客 6158

    linux基线检查、基线加固、安全漏扫、系统漏洞、centos7、文尾部附脚本

    等保三级-CentOS Linux 7合规基线检查 风险分类:系统-等保三级-CentOS Linux 7合规基线检查 检测项说明: CentOS Linux 7合规基线检查,对标中国等保2.0第三级等级保护基本要求部分检查项目,仅供参考 检查项目 : 应对登录的用户进行身份标识和鉴别,身份标识具有唯一性,身份鉴别信息具有复杂度要求并定期更换 加固建议: 1、执行`cat /etc/shadow | awk -F: '($2 == "" ) { print $1}' `, 查看空密码账户并处理; 2、

    swindy博客 5622

    Java线程间的通信方式

    **定义:**当多个线程共同操作共享的资源时,线程间通过某种方式互相告知自己的状态,以避免无效的资源争夺。通信的方式:**线程同步:**即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作。同步的话,仅仅传递的是控制信息,就是我什么时候运行结束,你什么时候可以来。对于线程间通信来说,线程间同步可以归纳为线程间通信的一个子集,对于线程通信指的是两个线程之间可以交换一些实时的数据信息,而线程同步只交换一些控制信息。在java中有几个关于

    Wdradon的博客 5822

    小米万兆路由器里的Docker安装可道云(Kodexplorer)私有网盘

    小米2022年12月份发布了万兆路由器,里面可以使用Docker。 今天尝试在小米的万兆路由器里安装[可道云](http://kodcloud.com/)(Kodexplorer)私有网盘。

    engchina的专栏 1万+

    《低功耗方法学》翻译——卷首语

    低功耗设计方法学。

    记录所学,分享知识,结识挚友 992

    java线程通信总结

    总结java线程通信的几种实现方式,包括volatile、Condition、wait/notify、await/signal等

    皓月星辰 612

    OpenHarmony之Histreamer引擎深度解析:pipeline_core架构如何全面取代GStreamer,一统音视频播放与录制

    Histreamer是OpenHarmony多媒体框架的核心媒体引擎,采用架构设计。一个轻量级的媒体引擎组件,提供播放、录制等场景的媒体数据流水线处理。1.播放场景分为如下几个节点:数据源读取、解封装、解码、输出;2.录制场景分为如下几个节点:数据源读取、编码、封装、输出。该引擎从OH5.0开始取代了原有的媒体引擎gstreamer(部分文档还保留较老架构的讲解),提供统一的音视频播放、录制、元数据解析等能力。

    m0_67505153的博客 332

    Java多线程间的通信

    Java多线程间的通信   Java还提供了一种线程间通信的机制,这种通信通什么实现? wait,notify等机制   或使用pipeInputStream和pipeOutputStream    1. 线程的几种状态 线程有四种状态,任何一个线程肯定处于这四种状态中的一种: 1) 产生(New):线程对象已经产生,但尚未被启动,所以无法执行。如通过new产生了一个线程对象后没

    it_man的专栏 2万+

    pscad IEEE标准系统搭建说明

    pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

    VC中sqlite的使用

    在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

    java线程通信

    Condition 提供了类似于 wait()、notify() 和 notifyAll() 的方法,但它们与 Lock 对象一起使用,提供了更灵活的线程通信机制。BlockingQueue 是Java中一个强大的接口,提供了线程安全的队列操作,并且可以在生产者-消费者模式中使用。两个线程可以在此同步点交换数据,Exchanger 的 exchange() 方法用于在两个线程之间交换数据。wait():使当前线程进入等待状态,直到其他线程调用 notify() 或 notifyAll()。

    1738

    假人有限元模型

    假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

    Java nio 的线程通信机制线程通信Pipe

    java nio 的线程通信机制Pipe

    m0_73700925的博客 624

    java线程间通信

    在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作。比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权。因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去。因此,一般情况下,当队列满时,会让生产者交出对临界...

    心猿意碼 1万+

    java 线程通信

    本篇聊聊java中的线程通信 文章目录线程通信1.1通信的方法1.2 sleep()和wait()的异同 线程通信 多个线程同时操作共享数据要求交流,就要用到通信。 1.1通信的方法 wait():调用次方法,阻塞线程,并释放锁 notify():唤醒被wait阻塞的线程。如果有多个阻塞的线程,唤醒优先级高的那个。 notifyAll();唤醒被wait阻塞的全部线程 例如:打印1到100的数,让两个线程交替打印。 class Window5 implements Runnable{ pri.

    hongguihuang的博客 197

    java线程间通信的方式

    java线程间通信的方式1. 共享变量2. 等待/通知3. 管道流 1. 共享变量 volatile修饰的变量,线程间可见,可使用这种变量作为线程间传递消息的媒介; 延伸出来的,还有redis中的值,数据库中的值,都可以作为线程间共同访问的变量; 2. 等待/通知 同步代码中利用锁对象的wait和notify方法来进行通信; 经典案例如thread.join()方法,里面就是用等待通知机制实现的; 3. 管道流 管道输入/输出流和普通的文件输入/输出流或者网络输入/输出流不同之处在于,它主要用于线

    没啥简介 5674

    java 线程通信和线程池

    Executor线程池顶级接口,只有一个方法真正的线程池接口执行任务/命令,没有返回值,一般用来执行Runnable执行任务,有返回值,一般又来执行Callable关闭线程池基本实现了ExecutorService的所有方法默认的线程池实现类实现周期性任务调度的线程池Executors工具类、线程池的工厂类,用于创建并返回不同类型的线程池创建可根据需要创建新线程的线程池创建一个可重用固定线程数的线程池创建一个只有一个线程的线程池}核心池的大小。...

    鑫阳的博客 1103

    Java 是如何实现线程间通信的?

    正常情况下,每个子线程完成各自的任务就可以结束了。不过有的时候,我们希望多个线程协同工作来完成某个任务,这时就涉及到了线程间通信了。本文涉及到的知识点:Callable。本文代码:https://github.com/wingjay/HelloJava/blob/master/multi-thread/src/ForArticle.java下面我从几个例子作为切入点来讲解下 Java 里有哪些方法来实现线程间通信。如何让两个线程依次执行?那如何让两个线程按照指定方式有序交叉运行呢?

    2501_92606851的博客 897

    Java 线程通信全解析

    使当前线程释放持有的对象锁,进入该对象的等待队列(wait set),并进入 WAITING 或 TIMED_WAITING 状态。BlockingQueue是线程安全的队列,提供了阻塞式的入队(put())和出队(take())方法,可直接用于线程通信,无需手动处理锁和等待/唤醒。这是最基础的通信方式,通过synchronized保证锁的独占性,wait()/notify()实现线程间的等待与唤醒。:使用内置锁机制,通过wait()让线程等待,notify()唤醒等待线程。

    Toroto5的博客 1372

    Java 多线程中的通信机制

    隐式锁:避免了手动加锁和释放锁。可重入锁:避免了死锁。简单易用:实现起来较为简单方便。不够灵活:加锁粒度过大。效率过低:相较于显示锁,synchronized 效率过低。不可中断:其他处于 BLOCKED 状态中的线程必须要等待锁的释放才可以被中断。

    2401_82656016的博客 1468

    线程间通信方式java_JAVA多线程之线程间的通信方式

    一,介绍本总结我对于JAVA多线程中线程之间的通信方式的理解,主要以代码结合文字的方式来讨论线程间的通信,故摘抄了书中的一些示例代码。二,线程间的通信方式①同步这里讲的同步是指多个线程通过synchronized关键字这种方式来实现线程间的通信。参考示例:public class MyObject {synchronized public void methodA() {//do somethin...

    weixin_35923682的博客 126
    上一篇: MySQL深分页的处理方案
    快乐吃手手  :  )
    博客等级 码龄6年 119粉丝 25原创
    评论
    成就一亿技术人!
    拼手气红包6.0元
    还能输入1000个字符
     
     条评论被折叠 查看
    添加红包

    请填写红包祝福语或标题

    个

    红包个数最小为10个

    元

    红包金额最低5元

    当前余额3.43元 前往充值 >
    需支付:10.00元
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付元
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值