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参…...

什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...

EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...

让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...

力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...