线程的几种通信方式

article/2025/9/24 18:42:24

目录

一、Object的wait()、notify()、notifyAll()方法

二、Condition的await()、signal()、signalAll()方法

三、CountDownLatch

四、CyclicBarrier

五、Semaphore


线程间的通信方式常用的有如下几种:

Object的wait()、notify()、notifyAll()方法;

Condition的await()、signal()、signalAll()方法;

CountDownLatch:用于某个线程A等待若干个其他线程执行完之后,它本身才会执行;

CyclicBarrier :一组线程等待至某个状态后再全部同时执行;

Semaphore:用于控制对某组资源的访问权限;

一、Object的wait()、notify()、notifyAll()方法

我们通过一个例子来理解这种线程间的通讯:开启两个线程,一个用来打印10以内的奇数,一个用来打印10以内的偶数。

代码如下:

public class ThreadComunicationDemo01 {private int i = 0;//要打印得数private Object obj = new Object();//奇数打印方法,由奇数线程调用public void odd(){//1.判断i是否小于10while(i < 10){synchronized(obj){if(i % 2 == 1){System.out.println(Thread.currentThread().getName() +"奇数:" + i);i++;//notify()方法和wait()必须放在synchronized同步代码块或方法中//否则会报IllegalMonitorStateException异常obj.notify();//唤醒偶数线程打印}else {try {obj.wait();//等待偶数线程打印完毕} catch (InterruptedException e) {e.printStackTrace();}}}}}//偶数打印方法,由偶数线程调用public void even(){//1.判断i是否小于10while(i < 10){synchronized (obj){if(i % 2 == 0){System.out.println(Thread.currentThread().getName() +"偶数:" + i);i++;//notify()方法和wait()必须放在synchronized同步代码块或方法中//否则会报IllegalMonitorStateException异常obj.notify();//唤醒奇数线程打印}else {try {obj.wait();//等待奇数线程打印完毕} catch (InterruptedException e) {e.printStackTrace();}}}}}public static void main(String[] args) {final ThreadComunicationDemo01 threadComunicationDemo01 = new ThreadComunicationDemo01();//1.开启奇数打印线程Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {threadComunicationDemo01.odd();}});//1.开启偶数打印线程Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {threadComunicationDemo01.even();}});thread1.start();thread2.start();}
}

输出结果:

Thread-1偶数:0
Thread-0奇数:1
Thread-1偶数:2
Thread-0奇数:3
Thread-1偶数:4
Thread-0奇数:5
Thread-1偶数:6
Thread-0奇数:7
Thread-1偶数:8
Thread-0奇数:9

二、Condition的await()、signal()、signalAll()方法

还是上面的例子我们用Condition来实现:

public class ThreadComunicationDemo02 {private int i = 0;//要打印得数private Lock lock = new ReentrantLock();private Condition condition = lock.newCondition();//奇数打印方法,由奇数线程调用public void odd(){//1.判断i是否小于10while(i < 10){lock.lock();try {if(i % 2 == 1){System.out.println(Thread.currentThread().getName() + "奇数:" + i);i++;condition.signal();//唤醒偶数线程打印}else {try {condition.await();//等待偶数线程打印完毕} catch (InterruptedException e) {e.printStackTrace();}}}finally {lock.unlock();}}}//偶数打印方法,由偶数线程调用public void even(){//1.判断i是否小于10while(i < 10){lock.lock();try{if(i % 2 == 0){System.out.println(Thread.currentThread().getName() + "偶数:" + i);i++;condition.signal();//唤醒奇数线程打印}else {try {condition.await();//等待奇数线程打印完毕} catch (InterruptedException e) {e.printStackTrace();}}}finally {lock.unlock();}}}public static void main(String[] args) {final ThreadComunicationDemo02 threadComunicationDemo01 = new ThreadComunicationDemo02();//1.开启奇数打印线程Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {threadComunicationDemo01.odd();}});//1.开启偶数打印线程Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {threadComunicationDemo01.even();}});thread1.start();thread2.start();}
}

输出结果:

Thread-1偶数:0
Thread-0奇数:1
Thread-1偶数:2
Thread-0奇数:3
Thread-1偶数:4
Thread-0奇数:5
Thread-1偶数:6
Thread-0奇数:7
Thread-1偶数:8
Thread-0奇数:9

可以看到通过Object的的wait()、notify()方法和通过Condition的await()、signal()方法,实现的效果是一样的。那么他们二者有什么差别吗?通过上面的代码,我们总结如下:

  • Object的wait()必须在synchronized(同步锁)下使用;
  • Object的wait()必须要通过notify()方法进行唤醒;
  • Condition的await()必须和Lock(互斥锁/共享锁)配合使用;
  • Codition的await()必须通过signal()方法进行唤醒;

三、CountDownLatch

CountDownLatch是在jdk1.5被引入的,存在于java.util.concurrent包下。CountDownLatch能够使一个线程等待其他线程都执行完后再进行本身的执行操作,它的这一功能是通过一个计数器来实现的,这个计数器的初始值一般就是“其他线程的数量”。我们通过一个图来理解一下CountDownLatch的执行原理:

 

                                          

如上图所示:假如一共有四个线程T1、T2、T3、TA,现在我们要求线程TA需要在其他的三个线程T1、T2、T3都执行完了以后才能执行。那初始时,我们设置CountDownLatch计数器的值为3,然后让线程TA调用CountDownLatch的await()方法,这个时候线程TA就会一直阻塞在哪里,在阻塞的过程中他会不断的去检查计数器的数量,如果不为0,那么它就一直阻塞在哪里。我们让其他三个线程正常执行,并且每个线程在执行完自身的操作后让其调用CountDownLatch的countDown()方法,每调用一次这个方法CountDownLatch的计数器数量就会减1。所以当T1、T2、T3这三个线程搜执行完后,countDown()这个方法就被调用了3次,CountDownLatch的计数器就会减为0,此时,被阻塞的线程TA检测到计数器的值为0后就会被唤醒,执行其本身的操作。

我们继续通过一个例子来说明CountDownLatch的使用:假设有三个运动员线程和一个教练线程,教练线程必须要等到三个运动员线程都准备好才能开始自身线程的训练工作。

代码如下:

public class ThreadComunicationDemo03 {private CountDownLatch countDownLatch = new CountDownLatch(3);//设置要等待得运动员是3个//运动员方法,由运动员线程调用public void racer(){//1.获取运动员名称(线程名)String racerName = Thread.currentThread().getName();//2.运动员开始准备:打印准备信息System.out.println(racerName + "运动员正在准备。。。");//3.让线程睡眠1000毫秒,表示运动员在准备try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//4.运动员准备完毕:打印准备完毕信息,同时计数-1System.out.println(racerName + "运动员准备完毕!");countDownLatch.countDown();}//教练方法,由教练线程调用public void coach(){//1.获取教练线程得名称String coachName = Thread.currentThread().getName();//2.教练等待所有运动员准备完毕,打印等待信息System.out.println(coachName + "教练等待运动员准备。。。");//3.调用countDownLatch得await()方法,等待其他运动员准备线程执行完毕try {countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}//4.所有运动员准备就绪,教练开始训练:打印训练信息System.out.println("所有运动员已经就绪," + coachName + "教练开始训练");}public static void main(String[] args) {//1.创建TThreadComunicationDemo03实例final ThreadComunicationDemo03 threadComunicationDemo03 = new ThreadComunicationDemo03();//2.创建三个线程对象,调用ThreadComunicationDemo03的racer方法Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {threadComunicationDemo03.racer();}});Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {threadComunicationDemo03.racer();}});Thread thread3 = new Thread(new Runnable() {@Overridepublic void run() {threadComunicationDemo03.racer();}});//3.创建一个线程对象,调用ThreadComunicationDemo03的coach方法Thread coachThread = new Thread(new Runnable() {@Overridepublic void run() {threadComunicationDemo03.coach();}},"教练");coachThread.start();thread1.start();thread2.start();thread3.start();}
}

输出结果:

hread-0运动员正在准备。。。
Thread-2运动员正在准备。。。
Thread-1运动员正在准备。。。
教练教练等待运动员准备。。。
Thread-2运动员准备完毕!
Thread-1运动员准备完毕!
Thread-0运动员准备完毕!
所有运动员已经就绪,教练教练开始训练

四、CyclicBarrier

CyclicBarrier也是在jdk1.5引入的,在java.util.concurrent包下。CyclicBarrier可以实现让“一组”线程等待至某个状态后再全部“同时”执行。CyclicBarrier底层是基于ReentrantLock和Codition实现的,有兴趣的同学可以找来源码看看。

我们继续通过一个例子来说明CyclicBarrier的使用:我们开启三个线程,并且等到这三个线程都处于就绪状态后同时让这三个线程一起执行。

public class ThreadComunicationDemo04 {//参数3是表示参与CyclicBarrier的线程数private CyclicBarrier cyclicBarrier = new CyclicBarrier(3);public void startThread(){//1.打印线程准备启动String name = Thread.currentThread().getName();System.out.println(name + "正在准备");//2.调用CyclicBarriar的await()方法等待线程全部准备完毕(所有线程在此处都会阻塞)try {cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}//3.打印线程启动完毕System.out.println(name + "已经启动完毕:" + new Date().getTime());}public static void main(String[] args) {final ThreadComunicationDemo04 threadComunicationDemo04 = new ThreadComunicationDemo04();Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {threadComunicationDemo04.startThread();}});Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {threadComunicationDemo04.startThread();}});Thread thread3 = new Thread(new Runnable() {@Overridepublic void run() {threadComunicationDemo04.startThread();}});thread1.start();thread2.start();thread3.start();}
}

输出结果:

Thread-0正在准备
Thread-1正在准备
Thread-2正在准备
Thread-2已经启动完毕:1582774772670
Thread-0已经启动完毕:1582774772670
Thread-1已经启动完毕:1582774772670

通过输出结果我们可以看到,这三个线程是同时执行的,都是在1582774772670这个时间点开始执行。

五、Semaphore

Semaphore也是在jdk1.5后引入的,同样也在java.util.concurrent包下。Semaphore主要是用于控制对某组资源的访问权限。这样说可能还是比较模糊,我们继续通过一个例子来说明Semaphore的使用:假设现在有8个工人3台机器,机器为互斥资源(即每次只能一个人使用),代码如下:

public class ThreadComunicationDemo05 {//内部类,当然你也可以新建一个文件定义在外面static class Work implements Runnable{private int workerNum;//工人的工号private Semaphore semaphore;//机器数public Work(int workerNum,Semaphore semaphore){this.semaphore = semaphore;this.workerNum = workerNum;}@Overridepublic void run() {try{//1.工人要去获取机器semaphore.acquire();//2.打印工人获取到机器,开始工作String workerName = Thread.currentThread().getName();System.out.println(workerName + "获取到机器,开始工作。。。");//3.线程睡眠1000毫秒,模拟工人使用机器的过程Thread.sleep(1000);//4.使用完毕,释放机器,打印工人使用完毕,释放机器semaphore.release();System.out.println(workerName + "使用完毕,释放机器");}catch (Exception e){e.printStackTrace();}}}public static void main(String[] args) {int workers = 8;//工人数是8个Semaphore semaphore = new Semaphore(3);//机器数是3个//i代表工人的工号,8个工人线程开启,并使用3台机器进行工作for (int i = 0; i < workers; i++){new Thread(new Work(i,semaphore)).start();}}
}

输出结果:

Thread-1获取到机器,开始工作。。。
Thread-0获取到机器,开始工作。。。
Thread-2获取到机器,开始工作。。。
Thread-1使用完毕,释放机器
Thread-4获取到机器,开始工作。。。
Thread-0使用完毕,释放机器
Thread-5获取到机器,开始工作。。。
Thread-2使用完毕,释放机器
Thread-3获取到机器,开始工作。。。
Thread-3使用完毕,释放机器
Thread-7获取到机器,开始工作。。。
Thread-6获取到机器,开始工作。。。
Thread-5使用完毕,释放机器
Thread-4使用完毕,释放机器
Thread-6使用完毕,释放机器
Thread-7使用完毕,释放机器

通过上面的输出结果我们可以看到,三个机器在八个工人手里正常的完成了工作,这就是Semaphore的作用所在。

就写先到这里吧!!!!!!!!!!!!!!!!


http://chatgpt.dhexx.cn/article/ghJBlIwj.shtml

相关文章

线程间的通信方法

线程间的通信方法 1. 线程通信简介 一般而言&#xff0c;在一个应用程序&#xff08;即进程&#xff09;中&#xff0c;一个线程往往不是孤立存在的&#xff0c;常常需要和其它线程通信&#xff0c;以执行特定的任务。如主线程和次线程&#xff0c;次线程与次线程&#xff0c…

Matlab基本操作函数 abs函数

分享一下我老师大神的人工智能教程&#xff01;零基础&#xff0c;通俗易懂&#xff01;http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章。分享知识&#xff0c;造福人民&#xff0c;实现我们中华民族伟大复兴&#xff01; 1、abs函数&#xff1a;数值的绝对值和复数…

MATLAB中FFT的整理

作为一个资深的健忘症患者&#xff0c;需要把每次用都忘记的FFT问题进行整理。 FFT可将信号从时域转换到频域。 首先是一些简单常识&#xff1a; 采样周期&#xff1a;两次采样之间的时间间隔。 采样频率&#xff1a;1/采样周期。每秒采样的点数。&#xff08;注意&#xff1a…

matlab中abs函数,matlababs是什么意思 是是是什么意思

matlababs是什么意思 是是是什么意思以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! matlab 中的abs函数什么意思 编程知识 matlab中的abs(x)是去绝对值的函数 例如:x=-1.23 abs(x) ans 1.23 以上即是取了-1.23…

Matlab 用法

MATLAB基础&#xff1a; 清除命令 clc 清空命令行的命令 clf 清除当前figure中的内容 clear 清除工作区变量 close all 关闭所有图形窗口 清除命令通常放在代码最前方&#xff0c;避免其他变量或代码的干扰 变量命名规则 ①以英文字母开头&#xff0c;可包含英文字母、…

abs 三种功能及代码详解 matlab函数

1.abs函数功能 求实数的绝对值、复数的模、字符串的ASCII值 2.基本用法 abs(x)函数是对数组元素进行绝对值处理的函数。 函数的定义域包括复数。 对于复数xab*i&#xff0c;有abs(x)sqrt(a2b2)。 3.代码 clc; clear all;a -7; b 12i; abs(a…

android 屏幕坐标总结

android 屏幕坐标好多个&#xff0c;有时候傻傻分不清楚&#xff0c;经常记错&#xff0c;然后只能一个个试。尴尬&#xff5e;&#xff5e; 把它们总结下来&#xff0c;以备不时之需嘿嘿。 一、视图坐标 最外面一层是屏幕&#xff0c;左上角是坐标原点&#xff0c;向右向…

【Unity3D】世界坐标与屏幕坐标

Unity3D由于是在三维世界中编程&#xff0c;而最终的结果是需要反馈到肉眼所示的2D屏幕之上的。这就产生了一种比较需要考虑的问题&#xff0c;尤其在一些涉及屏幕与Unity3D的3D世界交互的情况。网络上对于这方面的文字&#xff0c;大部分罗列了许许多多文字与代码或者API&…

Unity世界坐标转换屏幕坐标(测试)

下面展示一下上一篇说的两种实现方式打包文件在不同分辨率下的效果 1.WorldToScreenPoint 1920 * 1080 800 * 600 2.WorldToViewportPoint 1920 * 1080 800 * 600 总结 可以看到四种情况全部都显示正确&#xff0c;我们再看一下原来的代码 public Vector3 GetScreenPositio…

Unity 屏幕坐标转UI坐标

1&#xff1a;屏幕坐标转UI坐标 首先我们来明确下三个坐标概念&#xff1a; 世界坐标&#xff1a;指的是Transform组件的position字段 UI坐标&#xff1a;指的是RectTransform组件的anchoredPosition字段 屏幕坐标&#xff1a;指的是屏幕空间的坐标 (也可以说是相机空间的坐…

经纬度转换成屏幕坐标

学期projet总结&#xff1a; 当把点的数据和线的数据读进来之后&#xff0c;为了画出地图还有最重要的一步就是把实际的经纬度转换成屏幕像素点的坐标。在找老师讨论之前&#xff0c;我在网上查资料&#xff0c;找到了下边链接的文章&#xff0c;并按照这个方法画出了地图。 …

Unity-世界坐标与屏幕坐标

transform.position.x和transform.position.y的值含义是世界坐标。 世界坐标与屏幕坐标有时一样&#xff0c;有时不同&#xff0c;这和Canvas的渲染模式有关。 Canvas共有三种渲染模式 Screen Space - Overlay (此模式UGUI层一直在最上面&#xff0c;其他例如粒子等物体一直…

Unity 世界坐标、屏幕坐标、UGUI 坐标 相互转换

Unity 世界坐标、屏幕坐标、UGUI 坐标 相互转换 坐标转换是游戏开发过程中必不可少的环节 看下图 世界坐标、屏幕坐标、UI 坐标 三种坐标系的转换过程&#xff0c;此文章中的 UI 坐标特指 UGUI 坐标 从上图可以看到&#xff0c;世界坐标 和 UI 坐标 需要通过 屏幕坐标作为中间…

Android得到控件在屏幕中的坐标

getLocationOnScreen ,计算该视图在全局坐标系中的x,y值,(注意这个值是要从屏幕顶端算起,也就是索包括了通知栏的高度)//获取在当前屏幕内的绝对坐标 getLocationInWindow ,计算该视图在它所在的widnow的坐标x,y值,//获取在整个窗口内的绝对坐标 (不是很理解= =、) …

安卓 获取屏幕坐标(点击屏幕获取坐标)

工具下载&#xff1a; 实现原理&#xff1a;创建一个背景透明的Activity, 点击屏幕时获取坐标信息并显示。在悬浮窗中调用该Activity&#xff0c;可以获取所有界面的坐标信息。 package sc.tool.screen;import sc.tool.component.ActivityComponent; import android.content.Co…

Unity世界坐标转换屏幕坐标(详解)

我们先通过简单的操作实现一下基础的UI跟随物体移动的功能&#xff0c;首先我们在场景中建立一个Canvas并且添加一个图片作为按钮&#xff0c;之后我们添加一个3d物体作为跟随目标&#xff0c;效果如下图所示 我们配置一下UICanvas的属性&#xff0c;书写对应的自定义类并添加至…

地理坐标(经纬度坐标)和屏幕坐标(xy坐标)间的转换

在我们的屏幕上&#xff0c;有一张地图&#xff0c;这张地图经过缩放、平移、旋转&#xff0c;最终地理坐标和屏幕坐标的关系大致如下图所示&#xff1a; 这种关系要怎么描述呢&#xff1f;我们可以假设地图是一张纸&#xff0c;而屏幕是一堵墙。只要我们有两个图钉&#xff0c…

Windows的三种坐标系:屏幕坐标系,非客户区坐标系,客户区坐标系

1. 屏幕坐标系&#xff1a;以屏幕的左上角为原点&#xff0c;如图所示GetWindowRect() 函数获得的 RECT 就是以屏幕坐标系算的。 2. 非客户区坐标系(窗口坐标系)包括标题栏的部分。GetWindowDC 返回的设备环境就是基于此坐标系&#xff0c;一般只在 WM_NCPAINT 消息中使用。 3.…

Unity世界坐标系、本地坐标系、屏幕坐标系、视口坐标系

Unity中的坐标系 世界坐标系本地坐标系屏幕坐标系视口坐标系各个坐标系相互转换的API 世界坐标系 原点&#xff1a;世界的中心 轴向&#xff1a;世界坐标系的三个轴向是固定的 相关API: transform.position;transform.rotation; 四元数transform.eulerAngles; 欧拉角transfor…

Unity世界坐标转换屏幕坐标(概览)

这些天做的项目中&#xff0c;有一个世界坐标转换屏幕坐标的功能&#xff0c;具体需求如下 在3d场景中&#xff0c;部分物体可以进行点击交互&#xff0c;如果某个物体可以点击&#xff0c;就在屏幕上对应位置&#xff0c;添加一个Button&#xff0c;也就是把场景中物体的坐标转…