线程之间的通信

线程是如何通讯的? 线程通讯指的是多个线程之间通过共享内存或消息传递等方式来协调和同步它们的执行。在多线程编程中,通常会出现多个线程需要共同完成某个任务的情况,这时就需要线程之间进行通讯,以保证任务能够顺利地执行。线程通讯的实现方式主要有以下两种:共享内存:多个线程可以访问同一个共享内存区域,通过读取和写入内存中的数据来进行通讯和同步。消息传递:多个线程之间通过消息队列、管道、信号量等机制来传递信息和同步状态。 阅读详情

一、为什么要线程通信?

 

 1. 多个线程并发执行时, 在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务,

   并且我们希望他们有规律的执行, 那么多线程之间需要一些协调通信,以此来帮我们达到多线程共同操作一份数据。

2.当然如果我们没有使用线程通信来使用多线程共同操作同一份数据的话,虽然可以实现,

  但是在很大程度会造成多线程之间对同一共享变量的争夺,那样的话势必为造成很多错误和损失!

3.所以,我们才引出了线程之间的通信,多线程之间的通信能够避免对同一共享变量的争夺。

二、什么是线程通信?

  多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的使用或操作。

     就是多个线程在操作同一份数据时, 避免对同一共享变量的争夺。

  于是我们引出了等待唤醒机制:(wait()、notify())

  就是在一个线程进行了规定操作后,就进入等待状态(wait), 等待其他线程执行完他们的指定代码过后 再将其唤醒(notify);

 

  

(1)wait()方法:

在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。

    线程调用wait()方法,释放它对锁的拥有权,然后等待另外的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行。

    要确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中。

(2)notif()方法:

 notify()方法会唤醒一个等待当前对象的锁的线程。唤醒在此对象监视器上等待的单个线程。

(3)notifAll()方法:  notifyAll()方法会唤醒在此对象监视器上等待的所有线程。
   
(4)如果多个线程在等待,它们中的一个将会选择被唤醒。这种选择是随意的,和具体实现有关。(线程等待一个对象的锁是
由于调用了wait方法中的一个)

 notify()方法应该是被拥有对象的锁的线程所调用。

(5)以上方法都定义在类:Object中

1.因为,这些方法在操作同步中的线程的时候,都必须标示其所操作线程所持有的锁(被该锁的对象调用),

而只有同一个对象监视器下(同一个锁上)的被等待线程,可以被持有该锁的线程唤醒,(无法唤醒不同锁上的线程)

2.所以,等待和唤醒的必须是同一个对象的监视器①下(同一个锁上)的线程。而锁可以是任意已近确定的对象, 能被任意对象调用的方法应当定义在 Object类中。

注:①监视器(锁):同一个对象的监视器下(同一个锁上)的线程,一次只能执行一个:就是拥有监视器所有权(持有锁)的那一个线程。 
三、实现线程通信的小demo      

 如大众所说,体现线程通信的鲜明的例子:生产者---消费者是最明显的例子之一。那我们接下来敲一个关于生产者和消费者的简单的小demo,来演示一下线程的通信:

 (1)面包的类(生产和消费方法都在里面哦)

/**
 *@functon 线程通信之面包类 
 *@author 温煦(昵称:沉沦之巅)
 *@time 2017.12.5 
 */

package ThreadMessage;

public class Breads {
    
    //面包的id
    private int bid;
    //面包的个数
    private int num;
    
    //生产面包的方法(由于是demo,方便大家理解,就把synchronized关键字加到方法上面了哦)
    public synchronized void produc(){
        
        //当面包的数量不为0时,该方法处于等待状态
        if(0 != num){
            try {
                wait();//等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //当面包数量为0时,那么就开始生产面包了哦
        num  =  num +1;//数量加1
        bid = bid + 1 ;//id当然也得加1
        String threadname = Thread.currentThread().getName();
        System.out.println(threadname+"生产了一个编号为"+bid+"的面包!");
        notify();//当执行完后,去唤醒其他处于等待的线程
    }
    //消费面包的方法
    public synchronized void consume(){
        //当面包的数量为0时,该方法处于等待状态
        if(num == 0 ){
            try {
                wait();//等待
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        //消费完面包了,所以面包数量降为0了
        num  =  num -1;//数量减1
        String name1 = Thread.currentThread().getName();
        System.out.println(name1+"买了一个面包编号为"+bid);
        notify();//当执行完后,去唤醒其他处于等待的线程
    }

    
    //set和get方法
    public int getBid() {
        return bid;
    }

    public void setBid(int bid) {
        this.bid = bid;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    //有参构造
    public Breads(int bid, int num) {
        super();
        this.bid = bid;
        this.num = num;
    }

    //无参构造
    public Breads() {
        super();
        // TODO Auto-generated constructor stub
    }
}

(2)生产面包的类

/**
 *@functon 线程通信之生产类(继承Thread类) 
 *@author 温煦(昵称:沉沦之巅)
 *@time 2017.12.5 
 */

package ThreadMessage;

public class producer extends Thread{
    
    //获得面包的类
    private Breads bre ;

    //无参构造
    public producer() {
        super();
    }

    //有参构造
    public producer(Breads bre) {
        super();
        this.bre = bre;
    }


    //set和get方法
    public Breads getBre() {
        return bre;
    }

    public void setBre(Breads bre) {
        this.bre = bre;
    }

    //继承重写run方法
    @Override
    public void run() {
        pro();
    }
    
    //生产面包
    private void pro() {
        // 本系统默认循环生产20个面包
        for (int i = 0; i <20; i++) {
            try {
                //沉睡0.3秒(演示效果需要,可以不加)
                Thread.currentThread().sleep(300);
            } catch (InterruptedException e) {         
                e.printStackTrace();
            }
            //调用面包类里的生产面包的方法
            bre.produc();
        }
    }
}
(3)消费面包的类
/**
 *@functon 线程通信之消费类(继承Thread类) 
 *@author 温煦(昵称:沉沦之巅)
 *@time 2017.12.5 
 */

package ThreadMessage;

public class consume extends Thread{

    //获得面包的类
    private Breads bre ;
    
    //set和get方法
    public Breads getBre() {
        return bre;
    }
    public void setBre(Breads bre) {
        this.bre = bre;
    }
    
    //继承重写run方法
    @Override
    public void run() {
        con();
    }
    
    //消费面包
    private void con() {
        // 与生产者保持一致,本系统默认循环生产20个面包(生产几个,消费几个)
        for(int i = 0;i<20;i++){
            try {
                //沉睡0.3秒(演示效果需要,可以不加)
                Thread.currentThread().sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //调用面包类里的生产面包的方法
            bre.consume();
        }
    }

    //有参构造
    public consume(Breads bre) {
        super();
        this.bre = bre;
    }

    //无参构造
    public consume() {
        super();
    }
}

 (4)测试类

/**
 *@functon 线程通信之测试类 
 *@author 温煦(昵称:沉沦之巅)
 *@time 2017.12.5 
 */

package ThreadMessage;

public class TestBreads {
    
    public static void main(String[] args) {
        
        //new一个面包类
        Breads bre = new Breads();
        
        //new一个生产者类
        producer proth = new producer(bre);
        //new一个消费者类
        consume conth = new consume(bre);
        
        //new一个包含消费者类的线程
        Thread t1 = new Thread(proth,"生产者");
        
        //new一个包含生产者类的线程
        Thread t2 = new Thread(conth,"消费者");
        
        //启动线程
        t1.start();
        t2.start();
        
    }
}

 

   (5)演示效果(因数据太多,中间就省略了哦)

生产者生产了一个编号为1的面包!
消费者买了一个面包编号为1
生产者生产了一个编号为2的面包!
消费者买了一个面包编号为2
生产者生产了一个编号为3的面包!
消费者买了一个面包编号为3

...
...

 生产者生产了一个编号为17的面包!
 消费者买了一个面包编号为17
 生产者生产了一个编号为18的面包!
 消费者买了一个面包编号为18
 生产者生产了一个编号为19的面包!
 消费者买了一个面包编号为19
 生产者生产了一个编号为20的面包!
 消费者买了一个面包编号为20

 

   如上所示,案例表明生产者消费者之间就是运用了wait()和notify()这两个方法,通过通信的沉睡与唤醒机制来完成两个不同线程操作统一数据之间的通信,

当生产者生产出一个面包时,就会陷入沉睡,随后立即去唤醒消费者,就像是对他说,你去买吧,我生产好了,然后消费者就会屁颠屁颠的去买了那个面包,当他吃完那个面包后,

也会陷入沉睡,随后立即唤醒生产者,就像是对他说,你再生产一个吧,我吃完了,然后生产者就... ... 如此循环,周而复始,直到for循环结束为止。

  

四、多个生产者和消费者

  当我们创建多个生产者和消费者时,上述的代码就会出现一个问题,就是他们无法直到到底要唤醒哪一个,所以这时候我们就用到了notifAll()方法。

  那么代码就不一一再发了,与上面的差不多,唯一不一样的就是在面包类里的if换成while循环判断,notif()换成notifyAll()。

  然后再给大家写一下多个生产者和消费者的测试类:

  (1)多个生产者和消费者的测试类

/**
 *@functon 线程通信之测试类 
 *@author 温煦(昵称:沉沦之巅)
 *@time 2017.12.5 
 */

package ThreadMessage;

public class TestBreads {
    
    public static void main(String[] args) {
        
        //new一个面包类
        Breads bre = new Breads();
        
        //new一个生产者类
        producer proth = new producer(bre);
        //new一个消费者类
        consume conth = new consume(bre);
        
        //new三个包含消费者类的线程
        Thread t1 = new Thread(proth,"生产者1");
        Thread t3 = new Thread(proth,"生产者2");
        Thread t5 = new Thread(proth,"生产者3");
        
        //new三个包含生产者类的线程
        Thread t2 = new Thread(conth,"消费者1");
        Thread t4 = new Thread(conth,"消费者2");
        Thread t6 = new Thread(conth,"消费者3");
        
        //启动线程
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
    }
}

 

 

 (2)演示多个生产者和消费者通信效果

生产者1生产了一个编号为1的面包!
消费者3买了一个面包编号为1
生产者2生产了一个编号为2的面包!
消费者2买了一个面包编号为2
生产者3生产了一个编号为3的面包!
消费者1买了一个面包编号为3
生产者1生产了一个编号为4的面包!
消费者3买了一个面包编号为4

...
...

生产者3生产了一个编号为56的面包!
消费者3买了一个面包编号为56
生产者2生产了一个编号为57的面包!
消费者1买了一个面包编号为57
生产者1生产了一个编号为58的面包!
消费者2买了一个面包编号为58
生产者3生产了一个编号为59的面包!
消费者3买了一个面包编号为59
生产者2生产了一个编号为60的面包!
消费者1买了一个面包编号为60

    如上所示,我们创建了三个生产者和三个消费者,所以我们for循环为三次,也就是会生产和消费各60个面包;

  当进程开始后,会随机开启一个生产者线程生产一个面包后,陷入沉睡,随后会随机唤醒一个消费者线程,

  接着消费掉刚刚生产的那个面包,再次陷入沉睡,随机唤醒一个生产者线程...  周而复始,直到结束。

Windows Form 线程之间通信 Normal 0 7.8 磅 0 2 false false false MicrosoftInternetExplorer4 <!-- /* Sty 阅读详情

相关推荐

Java多线程之间的通信

在 Java 多线程编程中,​​线程通信​​是指多个线程之间通过某种机制进行数据交换或协调执行顺序,以确保线程间能够正确、高效地协作完成任务。由于线程是并发执行的独立个体,默认情况下它们之间是相互隔离的,但实际业务中常常需要线程间共享数据或同步操作(如生产者-消费者模型),这时就需要借助Java提供的通信机制来实现线程间的交互。

pan_junbiao的博客 1323

线程通信的四种方式

多个线程在并发执行的时候,他们在CPU中是随机切换执行的,这个时候我们想多个线程一起来完成一件任务,这个时候我们就需要线程之间的通信了,多个线程一起来完成一个任务,线程通信一般有4种方式: 通过 volatile 关键字 通过 Object类的 wait/notify 方法 通过 condition 的 await/signal 方法 通过 join 的方式 现在有一个问题,两个线程分别打印字符串,但是当线程A每输出两次的时候,线程B就输出一次,如此反复10次。 通过 volatile 关键字 通过

weixin_43786099的博客 3万+

在Android中两个子线程之间通信

Android中,相信主线程和子线程之间的通信大家都不陌生了吧。在一次面试经历中被问到了两个子线程之间是如何进行通信的。哎呦!这可蒙住我了。后来回家研究了下,分享给大家。 其实android中线程通信无非就是handler和looper的操作。 一般情况下的主线程和子线程之间的通信,都是通过主线程中的handler把子线程中的message发给主线程中的looper,或者,主线程...

tectrol的专栏 2431

线程间通讯(几种常见的通讯机制,以及实例)

介绍线程间通讯的几种方式,以及一些实例

bigger_belief的博客 1万+

薄膜电容寿命预测全解析:从理论到实践,5步搞定可靠性评估

本文全面解析薄膜电容寿命预测的理论与实践,提供五步可靠性评估流程,涵盖温度应力、电压应力等关键因素分析,并分享提升寿命的实用技巧。特别适用于新能源、电动汽车等领域的设计工程师,帮助准确预测薄膜电容在实际工况下的使用寿命。

weixin_33712987的博客 432

H3C VRF实验

POP-vpn-instance-RT1]route-distinguisher 11:11 # VPN实例-RT1的路由区分器值11:11。配置路由泄露,其实就是在一个VPN示例中添加另一个VPN实例的下一跳,比如在VPN实例RT1中添加去往SRV的默认路由。但是也有了一个新的问题,那就是RT1、RT2和RT3也能互访了,这让VPN实例变得没有意义了。[POP]ip vpn-instance RT1 #配置vpn实例-RT1。绑定了VPN实例,所以要把路由添加到VPN实例POP中。

m0_68698993的博客 4023

线程之间的通信方式

前言 我只是个搬运工,尊重原作者的劳动成果,本文来源下列文章链接: https://zhuanlan.zhihu.com/p/129374075 https://blog.youkuaiyun.com/jisuanji12306/article/details/86363390 线程之间为什么要通信? 通信的目的是为了更好的协作,线程无论是交替式执行,还是接力式执行,都需要进行通信告知。首先,要短信线程间通信的模型有两种:共享内存和消息传递,以下方式都是基本这两种模型来实现的。我们来基本一道面试常见的题目来分析: 题目

weixin_41979002的博客 7137

AD/Protel老手才知道的PCB过孔查看技巧:3分钟分清通孔、盲孔和埋孔

本文为PCB设计工程师提供了在Altium Designer/Protel中快速鉴别通孔、盲孔和埋孔的实用技巧。通过层叠管理器、高级筛选工具和实战五步法,帮助用户在3分钟内精准识别过孔类型,提升设计效率和电路性能。特别适合处理高密度HDI板卡中的复杂过孔布局。

weixin_29164497的博客 573

多线程之间通信

多线程之间通信概述多线程之间通信使用volatile实现通信使用synchronized,wait,notify/notifyAllCountDownLatch实现线程间通信 概述 马老师多线程视频学习总结(好记性不如烂笔头)。 多线程之间通信 使用volatile实现通信 模拟情形: 设计一个容器,两个线程。线程一给容器里面add元素,当容器中的元素个数为5个时,线程二结束。 未使用volati...

wz122330的博客 236

maxwell 平面变压器仿真(一)

在学习开关电源设计的时候,我们难免要设计变压器,采用有限元仿真软件MAXWELL 可以帮助我们确定变压器的励磁电感,漏感等参数。对于我们的理论分析大有裨益。但是苦于这破软件的教程真的是相当的零散,我相信大家用起来也总是苦于没有系统的教程指导,撰写本文的目的有两个,一个是记录自己使用的一些问题,一个是给大家提供一个可以参考的文档。

qq_45405656的博客 8244

【教程4>第5章>第19节】基于Gardner环的位同步系统MATLAB仿真与带时偏基带数据导出

由于在FPGA中,时偏的影响主要是由系统收发两端晶振的不同步带来的,所以在仿真测试阶段,很难直接通过FPGA来模拟这个因素,所以我们采用MATLAB模拟带时偏的基带输入信号,并将其导出,由FPGA调用作为测试信号源。可以看到,同步之后(图3)相对于带时偏的基带信号(图2),其更接近标准的数据(图1),且抖动较小。订阅过的朋友可以复习下这个课程,没有订阅的朋友,也可以直接复制如下的程序进行仿真测试。上述程序中,加入时偏的基带信号,通过子函数。同步之后,可以恢复出标准的QPSK星座图。

FPGA/MATLAB学习教程/源码/项目合作开发 520

进程、线程、协程的对比、区别和联系,进程之间的通信方式、线程之间的通信方式、协程之间的通信方式

学习了进程、线程协程的通信等

Ws_Te47的博客 1612

一台NAS两个系统:飞牛nas上完美运行群晖DSM?(飞牛nas+群晖nas套娃安装实操)

想在飞牛NAS上体验群晖DSM系统?💡 本文详细记录了飞牛nas上部署群晖的完整过程,从镜像转换到系统安装,每一步都有详细说明 🖥️,让你在不增加硬件成本的情况下,轻松实现一套硬件、多种系统的灵活部署 ✅

XiaoqiangClub的博客 2367

进程之间与线程之间的通信

序 本文主要是为了总结操作系统里面的进程与线程通信。 操作系统的主要任务是管理计算机的软件、硬件资源。现代操作系统的主要特点是多用户和多任务,也就是程序的并行执行,windows如此linux也是如此。所以操作系统就借助于进程来管理计算机的软、硬件资源,支持多任务的并行执行。要并行执行就需要多进程、多线程。因此多进程和多线程间为了完成一定的任务,就需要进行一定的通信。而线程间通信又和进程间的通信不同

vonmax007的博客 5891

量化交易 米筐 案例:市值因子选股策略

案例:市值因子的选股策略 1.结果 获得超额收益 年化 49% 2. 从股票中选择市值小的股票 选定财务数据筛选 进行每日调仓(多因子选股调仓 周期频率会小一些) 3. 代码 估值有关指标 # 在沪深300股票中,选出市值比较小的10只股票 # 在这个方法中编写任何的初始化逻辑。context对象将会在你的算法策略的任何方法之间做传递。 def init(context): # 沪深300组合 stocks = index_components('000300.XSHG') # 沪深3

廷益_飞鸟的博客 2311

用长tree方式做等长线

1)写一个sdc,把等长线的起点设置成clock source,用于创建create_clock,至于period和waveform等参数随便写即可。然后clock_opt -from build_clock -to route_clock即可。3)在正式cts之前或之后,给自己创建的scenario长tree并don't touch。前面讲过了如何用set_data_check做等长线,这里再讲一下如何用cts的方式做。2)为该clock创建一个新mode,并创建对应的scenario。

m0_61544122的博客 622

python 实现线程之间的通信

  前言:因为GIL的限制,python的线程是无法真正意义上并行的。相对于异步编程,其性能可以说不是一个等量级的。为什么我们还要学习多线程编程呢,虽然说异步编程好处多,但编程也较为复杂,逻辑不容易理解,学习成本和维护成本都比较高。毕竟我们大部分人还是适应同步编码的,除非一些需要高性能处理的地方采用异步。 首先普及下进程和线程的概念: 进程:进程是操作系统资源分配的基本单位。 ...

datong6583的博客 587

VC中sqlite的使用

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

进程之间的通信方式、线程之间的通信方式

进程和线程之间的通用方式

水w的博客 1896

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

两个线程之间是如何通信的呢?

wait()notify()和:基于对象监视器的等待/通知机制,需在同步代码块或方法中使用。Lock和Condition:提供了更灵活和细粒度的控制,通过显式的锁和条件对象进行线程同步。:内置的线程安全队列,实现了生产者-消费者模式,简化了线程间的通信和同步。选择合适的线程通信机制可以提高程序的效率和可靠性,并简化并发编程的复杂性。

喜欢猪猪 1073

在并发编程中:线程之间如何通信及线程之间如何同步?

线程之间如何通信 通信指线程之间以何种机制来交换信息。在命令式编程中,线程之间的通信机制有两种 共享内存 消息传递 共享内存 在共享内存的并发模型里,线程之间共享程序的公共状态,通过 写—读内存中的公共状态进行隐式通信。 消息传递 在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过发送消息来显式进行通信。 线程之间如何同步 同步是指程序中用于控制不同线程间操作发生相对顺序的机制。 在共享内存并发模型里,同步是显式进行的。程序员必须显式指定某个方法或者某段代码需要在线程之间互斥执行。 在消息传

藿香正气的博客 1121
上一篇: Spring Boot【快速入门】
下一篇: springMVC+ Mybatis 配置多数据源
rmnjava
博客等级 码龄20年 8粉丝 5原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值