当前位置:网站首页>4年工作經驗,多線程間的5種通信方式都說不出來,你敢信?
4年工作經驗,多線程間的5種通信方式都說不出來,你敢信?
2022-06-25 01:07:00 【Java知音_】
點擊關注公眾號,實用技術文章及時了解
問題
有兩個線程,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 基於 AQS 框架,相當於也是維護了一個線程間共享變量 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();
}
}來源:blog.csdn.net/ChineseSoftware/article/
details/118390388
推薦

PS:因為公眾號平臺更改了推送規則,如果不想錯過內容,記得讀完點一下“在看”,加個“星標”,這樣每次新文章推送才會第一時間出現在你的訂閱列錶裏。點“在看”支持我們吧!
边栏推荐
- 2022R1快开门式压力容器操作考题及答案
- Leetcode 1248. 统计「优美子数组」(害,突然发现只会暴力枚举了)
- QT (35) - operate excel qxlsx qaxobject
- Previous basic review (link)
- 【微服务|Sentinel】Sentinel快速入门|构建镜像|启动控制台
- VB 学习笔记
- Simulation questions and answers of the latest national fire facility operator (senior fire facility operator) in 2022
- 我想问一下兴业证券怎么开户?通过链接办理股票开户安全吗
- Picture rotation move zoom gradient
- ImageView shows network pictures
猜你喜欢

Première application de l'informatique quantique à la modélisation des flux de puissance dans les systèmes énergétiques à l'Université technique danoise

Source code analysis the problem that fragments cannot be displayed in the custom ViewGroup

2022年危险化学品经营单位安全管理人员考试试题及模拟考试

2022熔化焊接与热切割复训题库模拟考试平台操作

Custom animation (simulated win10 loading animation)
![[redis realizes seckill business ③] specific implementation of optimistic lock for oversold problem](/img/01/5ec4e5dae1748dce3d5dee33a24b0b.png)
[redis realizes seckill business ③] specific implementation of optimistic lock for oversold problem

【Redis实现秒杀业务④】一人一单,不可重复购买

Use of file class filenamefilter & filefilter in io

扎克伯格上手演示四款VR头显原型机,Meta透露元宇宙「家底」

Zuckerberg demonstrated four VR head display prototypes, and meta revealed the "family" of metauniverse
随机推荐
Activity startup process
Mobile security tool apktool
placeholder
Qiniu cloud uploads video to get the first frame of video
2022 melting welding and thermal cutting recurrent training question bank simulated examination platform operation
2022年起重机司机(限桥式起重机)考试题库模拟考试平台操作
Transform BeanUtils to achieve list data copy gracefully
Add information on the left and add parts on the right of the status bar
Xcode preview displays a bug in the content of the list view and its solution
Introduction to smart contract security audit delegatecall (2)
C#和C 的CAN通信实验
I 刷题 I — 复制带随机指针的链表
启动服务11111
Using bindservice method to pause music playing
LLVM TargetPassConfig
Sanic service startup failed
redis + lua实现分布式接口限流实现方案
EVM Brief
[micro service sentinel] real time monitoring | RT | throughput | concurrency | QPS
Source code analysis the problem that fragments cannot be displayed in the custom ViewGroup