当前位置:网站首页>4年工作经验,多线程间的5种通信方式都说不出来,你信吗?
4年工作经验,多线程间的5种通信方式都说不出来,你信吗?
2022-08-03 12:50:00 【技术琐事】
问题
有两个线程,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();
}
}
边栏推荐
- Last blog for July
- 基于php志愿者服务平台管理系统获取(php毕业设计)
- An动画基础之元件的图形动画与按钮动画
- The components of the basis of An animation movie clip animation between traditional filling
- OpenCV 透视变换
- Station B responded that "HR said that core users are all Loser": the interviewer was persuaded to quit at the end of last year and will learn lessons to strengthen management
- setTimeout 、setInterval、requestAnimationFrame
- PyTorch构建分类网络模型(Mnist数据集,全连接神经网络)
- GameFi 行业下滑但未出局| June Report
- B站回应“HR 称核心用户都是 Loser”:该面试官去年底已被劝退,会吸取教训加强管理
猜你喜欢
BOM系列之sessionStorage
PyTorch框架训练线性回归模型(CPU与GPU环境)
leetcode16最接近的三数之和 (排序+ 双指针)
An introduction to the skeleton tool
基于php家具销售管理系统获取(php毕业设计)
【蓝桥杯选拔赛真题48】Scratch跳舞机游戏 少儿编程scratch蓝桥杯选拔赛真题讲解
An introduction to basic tools for selecting line tools (package church)
Real number rounding and writing to file (C language file)
Unsupervised learning KMeans notes and examples
An animation optimization of shape tween and optimization of traditional tweening
随机推荐
业界新标杆!阿里开源自研高并发编程核心笔记(2022最新版)
An animation optimization of shape tween and optimization of traditional tweening
The common problems in the futures account summary
技术分享 | 接口自动化测试如何搞定 json 响应断言?
SQL分页查询_Sql根据某个字段分页
链游NFT元宇宙游戏系统开发技术方案及源码
滑动窗口的最大值
查看GCC版本_qt版本
为冲销量下探中低端市场,蔚来新品牌产品定价低至10万?
How does Filebeat maintain file state?
便携烙铁开源系统IronOS,支持多款便携DC, QC, PD供电烙铁,支持所有智能烙铁标准功能
An动画基础之散件动画原理与形状提示点
[微服务]多级缓存
Golang sync.WaitGroup
Unsupervised learning KMeans notes and examples
An introduction to basic tools for selecting line tools (package church)
漫画:怎么证明sleep不释放锁,而wait释放锁?
Autumn recruitment work
leetcode16 Sum of the closest three numbers (sort + double pointer)
HCIP第十五天笔记(企业网的三层架构、VLAN以及VLAN 的配置)