Java - 线程间的通信方式
线程通信的方式
线程中通信是指多个线程之间通过某种机制进行协调和交互
线程通信主要可以分为三种方式,分别为共享内存、消息传递和管道流。每种方式有不同的方法来实现
volatile共享内存
- 共享内存:线程之间共享程序的公共状态,线程之间通过读-写内存中的公共状态来隐式通信。
wait/notify等待通知方式
- 消息传递:线程之间没有公共的状态,线程之间必须通过明确的发送信息来显示的进行通信。
join方式
管道输入/输出流的形式
- 管道流
共享内存
/*** @Author: Simon Lang* @Date: 2020/5/5 15:13*/
public class TestVolatile {private static volatile boolean flag=true;public static void main(String[] args){new Thread(new Runnable() {public void run() {while (true){if(flag){System.out.println("线程A");flag=false;}}}}).start();
new Thread(new Runnable() {public void run() {while (true){if(!flag){System.out.println("线程B");flag=true;}}}}).start();}
}
测试结果:线程A和线程B交替执行
消息传递-线程等待和通知
线程等待和通知机制是线程通讯的主要手段之一。
在 Java 中有以下三种实现线程等待的手段 :
Object 类提供的 wait(),notify() 和 notifyAll() 方法;
Condition 类下的 await(),signal() 和 signalAll() 方法;
LockSupport 类下的 park() 和 unpark() 方法。
Object 类提供的 wait(),notify() 和 notifyAll() 方法;
Object lock = new Object(); new Thread(() -> {synchronized (lock) {try {System.out.println("线程1 -> 进入等待");lock.wait();System.out.println("线程1 -> 继续执行");} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程1 -> 执行完成");} }).start();Thread.sleep(1000); synchronized (lock) {// 唤醒线程System.out.println("执行 notifyAll()");lock.notifyAll(); }
Condition 类下的 await(),signal() 和 signalAll() 方法;
// 创建 Condition 对象 (lock 可创建多个 condition 对象) Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); // 加锁 lock.lock(); try {// 一个线程中执行 await()condition.await();// 另一个线程中执行 signal()condition.signal(); } catch (InterruptedException e) {e.printStackTrace(); } finally {lock.unlock(); }
Condition 类它可以创建出多个对象。那为什么有了 Object 类的 wait 和 notify 的方式,还需要 condition 来干嘛呢 ?
因为 Object 类的 wait 和 notify 只适用于一个任务队列,而 Condition 类的 await 和 signal 适用于多个任务队列,在多个任务队列的情况下,使用 Object 类的 wait 和 notify 可能会存在线程饿死的问题。
比如以上这种生产者消费者模型,当生产者,消费者(阻塞式的)都有多个的时候,并且此时任务队列里面没有任务了,所以消费者就会进入休眠状态,此时生产者需要做两件事情 :
将任务推送到任务队列
唤醒线程
【问题所在】① 此时如果使用 Object 类提供的 wait 和 notify,而唤醒线程是存在两种可能的:
1)唤醒了消费者
2)唤醒了生产者
如果是唤醒了生产者,那就出问题了,当生产者这边代码执行完了就结束了,消费者这边永远不会去消费队列里的任务了,这就会导致线程饥饿问题。
而 Condition 类因为可以被创建多个,所以可以使用两个 Condition 对象,一个指定唤醒生产者,一个指定唤醒消费者,这样就不会出现线程饥饿了。所以 Condition 类的 await 和 signal 是对 Object 类的 wait 和 notify 的一个补充,它解决了 Object 类种分组不明确的问题。
LockSupport 类下的 park() 和 unpark() 方法。
public static void main(String[] args) {Thread t1 = new Thread(() -> {LockSupport.park();System.out.println("线程1被唤醒");},"线程1");t1.start();Thread t2 = new Thread(() -> {LockSupport.park();System.out.println("线程2被唤醒");},"线程2");t2.start();Thread t3 = new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("唤醒线程2");LockSupport.unpark(t2);},"线程3");t3.start(); }
LockSupport 类又是对 Condition 类的一个补充,它可以指定唤醒某一个线程,它解决了前两种方式不能随机指定唤醒线程的问题。
join方式
join()方法的作用是:在当前线程A调用线程B的join()方法后,会让当前线程A阻塞,直到线程B的逻辑执行完成,A线程才会解除阻塞,然后继续执行自己的业务逻辑,这样做可以节省计算机中资源。
public class TestJoin {public static void main(String[] args){Thread thread=new Thread(new Runnable() {@Overridepublic void run() {System.out.println("线程0开始执行了");}});thread.start();for (int i=0;i<10;i++){JoinThread jt=new JoinThread(thread,i);jt.start();thread=jt;}
}
static class JoinThread extends Thread{private Thread thread;private int i;
public JoinThread(Thread thread,int i){this.thread=thread;this.i=i;}
@Overridepublic void run() {try {thread.join();System.out.println("线程"+(i+1)+"执行了");} catch (InterruptedException e) {e.printStackTrace();}}}
}
每个线程的终止的前提是前驱线程的终止,每个线程等待前驱线程终止后,才从join方法返回,实际上,这里涉及了等待/通知机制,即下一个线程的执行需要接受前驱线程结束的通知。
管道输入/输出流
管道流是是一种使用比较少的线程间通信方式,管道输入/输出流和普通文件输入/输出流或者网络输出/输出流不同之处在于,它主要用于线程之间的数据传输,传输的媒介为管道。
管道输入/输出流主要包括4种具体的实现:PipedOutputStrean、PipedInputStrean、PipedReader和PipedWriter,前两种面向字节,后两种面向字符。
java的管道的输入和输出实际上使用的是一个循环缓冲数组来实现的,默认为1024,输入流从这个数组中读取数据,输出流从这个数组中写入数据,当这个缓冲数组已满的时候,输出流所在的线程就会被阻塞,当向这个缓冲数组为空时,输入流所在的线程就会被阻塞。
public class TestPip {public static void main(String[] args) throws IOException {PipedWriter writer = new PipedWriter();PipedReader reader = new PipedReader();//使用connect方法将输入流和输出流连接起来writer.connect(reader);Thread printThread = new Thread(new Print(reader) , "PrintThread");//启动线程printThreadprintThread.start();int receive = 0;try{//读取输入的内容while((receive = System.in.read()) != -1){writer.write(receive);}}finally {writer.close();}}
private static class Print implements Runnable {private PipedReader reader;
public Print(PipedReader reader) {this.reader = reader;}
@Overridepublic void run() {int receive = 0;try{while ((receive = reader.read()) != -1){//字符转换System.out.print((char) receive);}}catch (IOException e) {System.out.print(e);}}}
}
对于Piped类型的流,必须先进性绑定,也就是调用connect()方法,如果没有将输入/输出流绑定起来,对于该流的访问将抛出异常。
相关文章:

Java - 线程间的通信方式
线程通信的方式 线程中通信是指多个线程之间通过某种机制进行协调和交互 线程通信主要可以分为三种方式,分别为共享内存、消息传递和管道流。每种方式有不同的方法来实现 共享内存:线程之间共享程序的公共状态,线程之间通过读-写内存中的公…...

【计算机网络】HTTP响应报文Cookie原理
目录 HTTP响应报文格式 一. 状态行 状态码与状态码描述 二. 响应头 Cookie原理 一. 前因 二. Cookie的状态管理 结束语 HTTP响应报文格式 HTTP响应报文分为四部分 状态行:包含三部分:协议版本,状态码,状态码描述响应头&a…...

2023年度盘点:智能汽车、自动驾驶、车联网必读书单
【文末送书】今天推荐几本自动驾驶领域优质书籍 前言 2023年,智能驾驶和新能源汽车行业仍然有着肉眼可见的新进展。自动驾驶技术继续尝试从辅助驾驶向自动驾驶的过渡,更重要的是相关技术成本的下降。根据《全球电动汽车展望2023》等行业报告,…...

一文讲解如何从 Clickhouse 迁移数据至 DolphinDB
ClickHouse 是 Yandex 公司于2016年开源的 OLAP 列式数据库管理系统,主要用于 WEB 流量分析。凭借面向列式存储、支持数据压缩、完备的 DBMS 功能、多核心并行处理的特点,ClickHouse 被广泛应用于广告流量、移动分析、网站分析等领域。 DolphinDB 是一款…...

[足式机器人]Part2 Dr. CAN学习笔记-数学基础Ch0-5Laplace Transform of Convolution卷积的拉普拉斯变换
本文仅供学习使用 本文参考: B站:DR_CAN Dr. CAN学习笔记-数学基础Ch0-5Laplace Transform of Convolution卷积的拉普拉斯变换 Laplace Transform : X ( s ) L [ x ( t ) ] ∫ 0 ∞ x ( t ) e − s t d t X\left( s \right) \mathcal{L} \left[ x\lef…...

生产问题: 利用线程Thread预加载数据缓存,其它类全局变量获取缓存偶发加载不到
生产问题: 利用线程Thread预加载数据缓存偶发加载不到 先上代码 public class ThreadTest {//本地缓存Map<String, Object> map new HashMap<String, Object>();class ThreadA implements Runnable{Overridepublic void run() {System.out.println("Thread…...
Elasticsearch mapping 之 性能相关配置
ES 常见类型 通用类型: 二进制: binary 布尔型: boolean 字符串: keyword, constant_keyword, wildcard, text 别名: alias 对象: object, flattened, nested, join 结构化数据类型: Range, ip, version, murmur3 空间数据类型: geo_point, geo_shape, point, shape 性…...
adb push报错:remote couldn‘t create file: Is a directory
adb push报错:remote couldn‘t create file: Is a directory 出现这个问题可能是电脑本地目录中包含中文或者是目录地址中多包含了一个/ 比如说以下两种路径 1. test/测试音频文件1/a.mp3 2.test/test_audio/ 这两种都是不可以的(我是在as中执行的…...
GitLab 服务更换了机器,IP 地址或域名没有变化时,可能会出现无法拉取或提交代码的情况。
当 GitLab 服务更换了机器,但 IP 地址或域名没有变化时,可能会出现无法拉取或提交代码的情况。 这可能是由于 SSH 密钥或 SSL 证书发生了变化。以下是一些可能的解决步骤: 这可能是由于 SSH 密钥或 SSL 证书发生了变化。以下是一些可能的解决…...

【华为OD题库-076】执行时长/GPU算力-Java
题目 为了充分发挥GPU算力,需要尽可能多的将任务交给GPU执行,现在有一个任务数组,数组元素表示在这1秒内新增的任务个数且每秒都有新增任务。 假设GPU最多一次执行n个任务,一次执行耗时1秒,在保证GPU不空闲情况下&…...

持续集成交付CICD:Jenkins使用GitLab共享库实现前后端项目Sonarqube
目录 一、实验 1.Jenkins使用GitLab共享库实现后端项目Sonarqube 2.优化GitLab共享库 3.Jenkins使用GitLab共享库实现前端项目Sonarqube 4.Jenkins通过插件方式进行优化 二、问题 1.sonar-scanner 未找到命令 2.npm 未找到命令 一、实验 1.Jenkins使用GitLab共享库实现…...

Linux文件结构与文件权限
基于centos了解Linux文件结构 了解一下文件类型 Linux采用的一切皆文件的思想,将硬件设备、软件等所有数据信息都以文件的形式呈现在用户面前,这就使得我们对计算机的管理更加方便。所以本篇文章会对Linux操作系统的文件结构和文件权限进行讲解。 首先…...

CentOS上安装和配置Apache HTTP服务器
在CentOS系统上安装和配置Apache HTTP服务器可以为您的网站提供可靠的托管环境。Apache是开源的Web服务器软件,具有广泛的支持和强大的功能。下面是在CentOS上安装和配置Apache HTTP服务器的步骤: 步骤一:安装Apache HTTP服务器 打开终端&am…...
前端知识(十二)———ES6迭代器
ES6中的迭代器是一种新的对象,它具有一个next()方法。next()方法返回一个对象,这个对象包含两个属性:value和done。value属性是迭代器中的下一个值,done属性是一个布尔值,表示迭代器是否已经遍历完所有的值。迭代器是一…...

云端仓库平台
SpringBoot MySQL Vue 等技术实现的云端仓库 技术栈 核心框架:SpringBoot 持久层框架:MyBatis-Plus 前端框架:Vue 数据库:MySQL 项目包含源码和数据库文件。 效果图如下:...
php第三方skd自动加载
把mugou-sdk复制到项目下在composer.josn找到classmap加入sdk "autoload": {"classmap": ["mugou-sdk"] },在composer.josn找到files加入sdk "autoload": {"files":[mugou-sdk] },项目目录下运行 composer dump-autoload…...

Golang channle(管道)基本介绍、快速入门
channel(管道)-基本介绍 为什么需要channel?前面使用全局变量加锁同步来解决goroutine的通讯,但不完美 1)主线程在等待所有goroutine全部完成的时间很难确定,我们这里设置10秒,仅仅是估算。 2)如果主线程休眠时间长了,…...

盘点六款颇具潜力的伪原创AI工具
写作作为信息传递的主要媒介,在庞大的信息海洋中,为了在激烈的竞争中脱颖而出,伪原创AI工具成为越来越多写手的神秘利器。在本文中,我们将深入盘点六款颇具潜力的伪原创AI工具,为你揭开它们神秘的面纱。 1. 文心一言 …...

基于SSM的健身房预约系统设计与实现
末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:Vue 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目:是 目录…...

postgresql自带指令命令系列二
简介 在安装postgresql数据库的时候会需要设置一个关于postgresql数据库的PATH变量 export PATH/home/postgres/pg/bin:$PATH,该变量会指向postgresql安装路径下的bin目录。这个安装目录和我们在进行编译的时候./configure --prefix [指定安装目录] 中的prefix参…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist
现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...
LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...
【Kafka】Kafka从入门到实战:构建高吞吐量分布式消息系统
Kafka从入门到实战:构建高吞吐量分布式消息系统 一、Kafka概述 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后成为Apache顶级项目。它被设计用于高吞吐量、低延迟的消息处理,能够处理来自多个生产者的海量数据,并将这些数据实时传递给消费者。 Kafka核心特…...