当前位置:网站首页>With 4 years of work experience, the 5 communication methods between multi-threads can't be said, can you believe it?
With 4 years of work experience, the 5 communication methods between multi-threads can't be said, can you believe it?
2022-08-03 22:11:00 【ah code】
问题
有两个线程,A 线程向一个集合里面依次添加元素“abc”字符串,一共添加十次,当添加到第五次的时候,希望 B 线程能够收到 A 线程的通知,然后 B 线程执行相关的业务操作.线程间通信的模型有两种:共享内存和消息传递,以下方式都是基本这两种模型来实现的.
一、使用 volatile 关键字
基于 volatile 关键字来实现线程间相互通信是使用共享内存的思想.大致意思就是多个线程同时监听一个变量,当这个变量发生变化的时候 ,线程能够感知并执行相应的业务.这也是最简单的一种实现方式
public class TestSync {//定义共享变量来实现通信,它需要volatile修饰,否则线程不能及时感知
static volatile boolean notice = false;
public static void main(String[] args) {
List<String> list = new ArrayList<>();
//线程A
Thread threadA = new Thread(() -> {
for ( int i = 1; i <= 10; i++) {
list.add( "abc");
System.out.println( "线程A添加元素,此时list的size为:" + list.size());
try {
Thread.sleep( 500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
notice = true;
}
});
//线程B
Thread threadB = new Thread(() -> {
while ( true) {
if (notice) {
System.out.println( "线程B收到通知,开始执行自己的业务...");
break;
}
}
});
//需要先启动线程B
threadB.start();
try {
Thread.sleep( 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 再启动线程A
threadA.start();
}
}
二、使用 Object 类的 wait()/notify()
Object 类提供了线程间通信的方法:wait()、notify()、notifyAll(),它们是多线程通信的基础,而这种实现方式的思想自然是线程间通信.
注意:wait/notify 必须配合 synchronized 使用,wait 方法释放锁,notify 方法不释放锁.wait 是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行,只有其他线程调用了notify(),notify并不释放锁,只是告诉调用过wait()的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为锁还在别人手里,别人还没释放,调用 wait() 的一个或多个线程就会解除 wait 状态,重新参与竞争对象锁,程序如果可以再次得到锁,就可以继续向下运行.
public class TestSync {public static void main(String[] args) {
//定义一个锁对象
Object lock = new Object();
List<String> list = new ArrayList<>();
// 线程A
Thread threadA = new Thread(() -> {
synchronized (lock) {
for ( int i = 1; i <= 10; i++) {
list.add( "abc");
System.out.println( "线程A添加元素,此时list的size为:" + list.size());
try {
Thread.sleep( 500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
lock.notify(); //唤醒B线程
}
}
});
//线程B
Thread threadB = new Thread(() -> {
while ( true) {
synchronized (lock) {
if (list.size() != 5) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println( "线程B收到通知,开始执行自己的业务...");
}
}
});
//需要先启动线程B
threadB.start();
try {
Thread.sleep( 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//再启动线程A
threadA.start();
}
}
由输出结果,在线程 A 发出 notify() 唤醒通知之后,依然是走完了自己线程的业务之后,线程 B 才开始执行,正好说明 notify() 不释放锁,而 wait() 释放锁.
三、使用JUC工具类 CountDownLatch
jdk1.5 之后在java.util.concurrent包下提供了很多并发编程相关的工具类,简化了并发编程代码的书写,CountDownLatch 基于 ,相当于也是维护了一个线程间共享变量 state.
public class TestSync {public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch( 1);
List<String> list = new ArrayList<>();
//线程A
Thread threadA = new Thread(() -> {
for ( int i = 1; i <= 10; i++) {
list.add( "abc");
System.out.println( "线程A添加元素,此时list的size为:" + list.size());
try {
Thread.sleep( 500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
countDownLatch.countDown();
}
});
//线程B
Thread threadB = new Thread(() -> {
while ( true) {
if (list.size() != 5) {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println( "线程B收到通知,开始执行自己的业务...");
break;
}
});
//需要先启动线程B
threadB.start();
try {
Thread.sleep( 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//再启动线程A
threadA.start();
}
}
四、使用 ReentrantLock 结合 Condition
public class TestSync {public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
List<String> list = new ArrayList<>();
//线程A
Thread threadA = new Thread(() -> {
lock.lock();
for ( int i = 1; i <= 10; i++) {
list.add( "abc");
System.out.println( "线程A添加元素,此时list的size为:" + list.size());
try {
Thread.sleep( 500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
condition.signal();
}
lock.unlock();
});
//线程B
Thread threadB = new Thread(() -> {
lock.lock();
if (list.size() != 5) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println( "线程B收到通知,开始执行自己的业务...");
lock.unlock();
});
threadB.start();
try {
Thread.sleep( 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadA.start();
}
}
这种方式使用起来并不是很好,代码编写复杂,而且线程 B 在被 A 唤醒之后由于没有获取锁还是不能立即执行,也就是说,A 在唤醒操作之后,并不释放锁.这种方法跟 Object 的 wait()/notify() 一样.
五、基本 LockSupport 实现线程间的阻塞和唤醒
LockSupport 是一种非常灵活的实现线程间阻塞和唤醒的工具,使用它不用关注是等待线程先进行还是唤醒线程先运行,但是得知道线程的名字.
public class TestSync {public static void main(String[] args) {
List<String> list = new ArrayList<>();
//线程B
final Thread threadB = new Thread(() -> {
if (list.size() != 5) {
LockSupport.park();
}
System.out.println( "线程B收到通知,开始执行自己的业务...");
});
//线程A
Thread threadA = new Thread(() -> {
for ( int i = 1; i <= 10; i++) {
list.add( "abc");
System.out.println( "线程A添加元素,此时list的size为:" + list.size());
try {
Thread.sleep( 500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5)
LockSupport.unpark(threadB);
}
});
threadA.start();
threadB.start();
}
}
边栏推荐
- 【进阶自动化测试】一文1000教你如何用Postman做接口自动化测试
- Conditional Statements for Shell Programming
- Optimize the query (work in progress)
- 113. Teach a Man how to fish - How to query the documentation and technical implementation details of any SAP UI5 control property by yourself
- 483. Smallest Good Base
- Pay from 0 to 1
- 易基因|RNA m5C甲基化测序(RNA-BS)技术介绍
- How to write a database document management tool based on WPF (2)
- LyScript 实现应用层钩子扫描器
- Cisco ike2 IPSec configuration
猜你喜欢

Gains double award | know micro easily won the "2021 China digital twin solution suppliers in excellence" "made in China's smart excellent recommended products" double award!

CAS:1192802-98-4_UV 裂解的生物素-PEG2-叠氮

CAS:122567-66-2_DSPE-Biotin_DSPE-Biotin

CAS:1797415-74-7_TAMRA-Azide-PEG-Biotin

封装、包、访问权限修饰符、static变量

LabVIEW代码生成错误 61056

1 秒完成授权,Authing 全新上线一键登录功能
![[kali-vulnerability scanning] (2.1) Nessus lifts IP restrictions, scans quickly without results, and plugins are deleted (middle)](/img/93/0b78b6a930380aeecfbbb156df7498.png)
[kali-vulnerability scanning] (2.1) Nessus lifts IP restrictions, scans quickly without results, and plugins are deleted (middle)

CAS: 773888-45-2_BIOTIN ALKYNE_生物素-炔基

嵌入式系统:GPIO
随机推荐
网络基础学习系列四(网络层,数据链路层和一些其他重要协议或技术)
七夕快乐!
Cross-end development technical reserve record
How to deal with commas in the content of the CSV file of the system operation and maintenance series
Internet user account information management regulations come into effect today: must crack down on account trading and gray products
CAS:908007-17-0_Biotin-azide_Biotin azide
HCIP第十三天
2022-08-03 oracle执行慢SQL-Q17对比
2022-08-02 mysql/stonedb slow SQL-Q18 - memory usage surge analysis
2022-08-02 mysql/stonedb慢SQL-Q18-内存使用暴涨分析
Embedded Systems: GPIO
21天打卡挑战学习MySQL——《Window下安装MySql》第一周 第三篇
用于流动质押和收益生成的 Web3 基础设施
VLAN实验
L2-041 插松枝
113. Teach a Man how to fish - How to query the documentation and technical implementation details of any SAP UI5 control property by yourself
Bytebase database schema change management tool
Bytebase数据库 Schema 变更管理工具
Golang第二章:程序结构
Kubernetes入门到精通-Operator 模式