Lock實現執行緒間定製化通訊
合集 - JUC基礎(13)
1.
JUC前置知識
2023-10-23
2.
Synchronized和Lock介面
2023-10-25
3.
執行緒間通訊
2023-10-28
4.
Lock實現執行緒間定製化通訊
2023-10-30
5.
常用集合執行緒安全分析
2023-11-02
6.
多執行緒鎖
2023-11-12
7.
Callable介面和Future介面
2023-11-20
8.
JUC的強大輔助類
2023-11-22
9.
ReentrantReadWriteLock讀寫鎖
2023-11-24
10.
BlockingQueue阻塞佇列
2023-11-26
11.
執行緒池
2023-11-28
12.
Fork/Join
2023-12-01
13.
CompletableFuture非同步回撥
2023-12-01
收起
Lock實現執行緒間定製化通訊
案例
要求
三個執行緒,AA BB CC AA執行緒列印5次,BB執行緒列印10次,CC執行緒列印15次
程式碼實現
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
-
@author 長名06
-
@version 1.0
-
執行緒定製化通訊
*/
//第一步,建立共享資源,和操作方法
class ShareFlag {private Lock lock = new ReentrantLock();
private int flag = 1;private Condition c1 = lock.newCondition();//一個Condition物件,只能喚醒由該物件阻塞的執行緒
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();public void print5(int loop) throws InterruptedException {
lock.lock();try { //第四步,避免虛假喚醒現象 while (flag != 1) {//第2.1步 判斷是否滿足執行緒工作條件,阻塞 c1.await(); } //第2.2步 具體執行 for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "\t" + i +"次輸出" + "flag=" + flag + "輪數" + loop); } flag = 2; //通知(喚醒等待執行緒) c2.signal(); } finally { lock.unlock(); }
}
public void print10(int loop) throws InterruptedException {
lock.lock();try { while (flag != 2) { c2.await(); } for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "\t" + i +"次輸出" + "flag=" + flag + "輪數" + loop); } flag = 3; //通知(喚醒等待執行緒) c3.signal(); } finally { lock.unlock(); }
}
public void print15(int loop) throws InterruptedException {
lock.lock();try { while (flag != 3) { c3.await(); } for (int i = 0; i < 15; i++) { System.out.println(Thread.currentThread().getName() + "\t" + i +"次輸出" + "flag=" + flag + "輪數" + loop); } flag = 1; //通知(喚醒等待執行緒)
//程式碼效果參考:https://www.weibow.com/sitemap/post.xml
c1.signal();
} finally {
lock.unlock();
}
}
}
public class ThreadDemoByCust {
public static void main(String[] args) {
ShareFlag shareFlag = new ShareFlag();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
shareFlag.print5(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "AA").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
shareFlag.print10(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "BB").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
shareFlag.print15(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "CC").start();
}
}
學習時,關於程式碼的疑問
不理解,為什麼一定是,AA執行緒執行完後,,BB執行緒執行完後,是c3是c2去執行喚醒一個等待執行緒操作去執行喚醒一個等待執行緒的操作,CC執行緒執行完後,是c1執行喚醒一個等待執行緒的操作。先往後面看,後續回來解答這個問題。
解答
是為了滿足要求中的順序,最開始三個AA,BB,CC執行緒,並行執行,因為flag初始值是1,所以AA先執行第一次迴圈,執行print5,然後迴圈輸出5次後,flag = 2了(AA被c1阻塞),為了保證要求,再BB輸出10次,則需要使用c2去喚醒BB執行緒,為什麼是c2,因為BB執行緒在,最開始flag = 1時,是由c2去阻塞的,這裡如果是c3物件阻塞的,則必須要用c3物件喚醒,因為Condition物件,只能喚醒由該物件阻塞的執行緒。後面BB執行緒切換CC執行緒,CC執行緒切換AA執行緒也是同理。
只是為了記錄自己的學習歷程,且本人水平有限,不對之處,請指正。