一、JUC學習準備
核心知識點:進程、線程、并發(共享模型、非共享模型)、并行
預備知識:
基于JDK8,對函數式編程、lambda有一定了解
采用了slf4j打印日志
采用了lombok簡化java bean編寫
二、進程與線程
進程和線程概念
兩者對比
并行和并發
操作系統cpu任務調度器
同一時間能夠應對多件事件的能力稱為并發
應用
三、java線程
創建線程
方法一:使用Thread
創建線程對象
給線程命名t1
啟動線程
@Slf4j(topic = "c.Test1")
public class Test1 {public static void main(String[] args) {Thread t = new Thread(){@Overridepublic void run() {log.debug("running");}};t.setName("t1");t.start();log.debug("running");}
}
方法二:使用Runnable
把線程和任務分開
@Slf4j(topic = "c.Test2")
public class Test2 {public static void main(String[] args) {Runnable r = new Runnable() {@Overridepublic void run() {log.debug("running");}};Thread t = new Thread(r,"t2");t.start();}
}
使用lambda簡化
idea快捷鍵alt+enter
@Slf4j(topic = "c.Test2")
public class Test2 {public static void main(String[] args) {Runnable r = () -> log.debug("running");Thread t = new Thread(r,"t2");t.start();}
}
Thread與Runnable的關系
?
方法三:使用FutureTask
間接使用了Runnable
能夠接收Callable
可以把任務的結果傳給其他線程
@Slf4j(topic = "c.Test3")
public class Test3 {public static void main(String[] args) throws ExecutionException, InterruptedException {FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {@Overridepublic Integer call() throws Exception {log.debug("running");Thread.sleep(1000);return 0;}});Thread t1 = new Thread(task, "t1");t1.start();log.debug("{}", task.get());}
}
線程運行
現象-兩個線程交替運行
@Slf4j(topic = "c.TestMultiThread")
public class Test4 {public static void main(String[] args) throws ExecutionException, InterruptedException {new Thread(() -> {while (true) {log.debug("running");}}, "t1").start();new Thread(() -> {while (true) {log.debug("running");}}, "t2").start();}
}
查看進程線程
線程運行原理
原理
圖解
多線程
多線程的棧楨之間互不干擾,相互獨立
debug時采用Thread查看
線程上下文切換
線程API
start和run比較
start主要作用啟動另一個線程調用run方法,如果直接用創建的線程對象調用run方法,占用的還是主線程。
創建線程后,調用start前處于new狀態,未被cpu運行
創建線程后,調用start后處于runnable狀態,可以被cpu調度運行
sleep與yield
sleep會讓當前調用的線程進入睡眠
睡眠結束后要等待cpu時間片(任務調度器)分配才能得到執行
線程優先級
public class Test6 {public static void main(String[] args) {Runnable task1 = () -> {int count = 0;while (true) {System.out.println("t1---------->" + count++);}};Runnable task2 = () -> {int count = 0;while (true) {//Thread.yield();System.out.println(" t2---------->" + count++);}};Thread t1 = new Thread(task1,"t1");Thread t2 = new Thread(task2,"t2");t1.setPriority(Thread.MAX_PRIORITY);t2.setPriority(Thread.MIN_PRIORITY);t1.start();t2.start();}}
sleep應用
join方法
@Slf4j(topic = "c.Test7")
public class Test7 {static int r = 0;public static void main(String[] args) {test();}private static void test() {log.debug("main start...");Thread t1 = new Thread(() -> {log.debug("t1 start...");try {sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}log.debug("t1 end...");r = 10;}, "t1");t1.start();log.debug("結果:{}", r);log.debug("main end...");}
}
join方法作用:等待調用join方法的線程運行結束
在start后加join即可
t1.start();
t1.join();
應用同步
@Slf4j(topic = "c.Test8")
public class Test8 {static int r1 = 0;static int r2 = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {try {sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}r1 = 10;}, "t1");Thread t2 = new Thread(() -> {try {sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}r2 = 20;}, "t2");t1.start();t2.start();long start = System.currentTimeMillis();log.debug("join begin");t1.join();log.debug("t1 join end");t2.join();log.debug("t2 join end");long end = System.currentTimeMillis();log.debug("r1:{},r2:{},cost:{}", r1, r2, end - start);}
}
多線程start時已經開始運行,當join調用時,如果線程已經運行結束,則不會等待。
限時同步
超過join的等待時間,則不會等待線程的結果。
如果設置的join等待時間內,線程提前結束,也會提前結束等待。
interrupt方法
打斷sleep,wait,join的阻塞線程
打斷特殊的阻塞線程會清空打斷狀態,打斷狀態重置為false,正常為true
打斷正常的運行線程
主線程調用interrupt方法只是通知打斷t1線程,但不會真的打斷,此時t1線程的打斷標記為true,t1線程可以通過打斷標記決定是否中斷自己。
@Slf4j(topic = "c.Test9")
public class Test9 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (true) {boolean interrupted = Thread.currentThread().isInterrupted();if (interrupted) {log.debug("線程被中斷");break;}}}, "t1");t1.start();sleep(1000);log.debug("打斷t1線程");t1.interrupt();}
}
設計模式:兩階段終止模式
1.啟動監控線程
2.定時循環執行監控記錄
3.在循環過程中判斷監控線程是否被打斷
4.打斷標記為true時執行打斷操作
@Slf4j(topic = "c.Test10")
public class Test10 {public static void main(String[] args) throws InterruptedException {TwoPhaseTermination tpt = new TwoPhaseTermination();tpt.start();sleep(3500);tpt.stop();}
}
@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination {private Thread monitor;//啟動監控線程public void start() {monitor = new Thread(() -> {while (true) {Thread current = Thread.currentThread();if(current.isInterrupted()) {log.debug("料理后事");break;}try {sleep(2000);//情況一:阻塞被打斷log.debug("執行監控記錄");//情況二:正常運行被打斷} catch (InterruptedException e) {e.printStackTrace();monitor.interrupt();}}});monitor.start();}//停止監控線程public void stop() {monitor.interrupt();}
}
打斷park
park被打斷后,此時打斷狀態為true,如果繼續執行park會失效,可以將狀態設為false后恢復。
過時方法
守護線程
線程狀態
五種狀態
六種狀態
?
四、共享模型之管程
悲觀鎖思想解決并發問題
共享問題
由線程上下文切換引起指令交錯導致的多線程訪問共享資源的線程安全問題
靜態變量 i 存儲在主內存中,多個線程都可以讀取 i 值,計算是在線程中完成,完成后需寫回主內存。如果計算完成還未寫入主存時出現上下文切換則會導致線程安全問題。
臨界區
競態條件

synchronized
需要一個多線程共享的對象
語法
@Slf4j(topic = "c.Test11")
public class Test11 {static int count = 0;static final Object lock = new Object();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 5000; i++) {synchronized (lock) {count++;}}}, "t1");Thread t2 = new Thread(() -> {for (int i = 0; i < 5000; i++) {synchronized (lock) {count--;}}}, "t2");t1.start();t2.start();t1.join();t2.join();log.debug("count:{}", count);}
}
理解
線程1獲取鎖對象后,如果臨界區還未執行完,發生上下文切換到其他線程獲取不到會被blocked,繼續上下文切換,直到臨界區代碼執行結束釋放鎖,喚醒阻塞的線程,其他線程才能獲取到鎖。
synchronized放在for循環外面,會保證整個循環的原子性
如果多線程獲取的不是同一個鎖對象,不能保證線程安全;要給同一對象加鎖保護共享資源
如果t1加鎖,t2未加鎖,上下文切換到t2時,此時不需要獲取對象鎖,則不會被阻塞,t2線程會運行
鎖對象面向對象改進
@Slf4j(topic = "c.Test12")
public class Test12 {public static void main(String[] args) throws InterruptedException {Room room = new Room();Thread t1 = new Thread(() -> {for (int i = 0; i < 5000; i++) {room.add();}}, "t1");Thread t2 = new Thread(() -> {for (int i = 0; i < 5000; i++) {room.reduce();}}, "t2");t1.start();t2.start();t1.join();t2.join();log.debug("count:{}", room.getCount());}
}@Slf4j(topic = "c.room")
class Room {private static int count;public void add() {synchronized (this) {count++;}}public void reduce() {synchronized (this) {count--;}}public int getCount() {synchronized (this) {return count;}}
}
方法上的synchronized
成員方法:鎖this對象
靜態方法:鎖類對象

線程安全分析
變量線程安全分析
局部變量的自增和成員變量自增不同。局部變量只用了一步
局部變量在棧中互相獨立
子類繼承父類并重寫方法,可能會再加一個線程,此時有多個線程訪問共享資源list,造成局部變量引用暴露給了其它線程,有線程安全問題
private修飾父類方法會限制子類不能覆蓋
final修飾父類方法防止子類重寫
常見線程安全類
多方法組合不能保證原子性
不可變線程安全性
通過如下源碼可知,這些方法重新創建了一個新的字符串對象,并把新值賦給新字符串的value,沒有改變原有的String對象
無狀態(成員變量)的類一般是線程安全的
如果有成員變量,最好變成局部變量,否則要看多線程訪問是否是同一個
如果是成員變量,看是否向外暴露引用。如下所示局部變量向外泄露導致并發不安全
Monitor概念
java對象頭
對象頭占8個字節,對象的類型通過Klass Word指針找到
Monitor工作原理
圖文角度
字節碼角度
synchronized進階原理
輕量級鎖
加鎖、鎖重入、解鎖
鎖膨脹
自旋優化
鎖競爭發生阻塞會進行上下文切換比較耗時,通過自旋(循環重試獲取鎖)優化
自旋需要cpu,不適合單核。
偏向鎖
加偏向鎖前、中、后測試代碼
測試hashCode()
調用hashCode會禁用偏向鎖,因為對象頭沒有空間存儲31位hashcode,需要將偏向狀態變成正常狀態,再將hashcode放進去。
在輕量級鎖中,hashcode存儲在線程棧楨的鎖記錄中
在重量級鎖中,hashcode存儲在monitor對象中
讓兩個線程錯開訪問鎖對象,如果發生交替會產生鎖競爭
批量重偏向
批量撤銷
鎖消除
java代碼在編譯時會對代碼進行鎖優化,如果發現代碼中加的鎖沒有任何意義,JIT會把鎖優化掉。
如圖局部變量o不會逃離方法作用范圍,表明對象不會被共享,代碼真正執行時沒有sychronized。
鎖消除禁用:-XX:-EliminateLocks
wait/notify
當一個線程占著鎖后,由于需要一些條件暫時無法工作,導致后續想獲取鎖的線程阻塞,此時用wait方法該線程進入waitset并釋放鎖,讓其他線程運行。等待條件滿足通知該線程離開waitset,進入競爭鎖隊列entrylist
工作原理
API
線程必須先獲取到鎖對象后才能調用wait方法,進入該鎖對象的休息室
wait(1000)代表等待一秒后,退出等待,如果等待時間內被喚醒,則提前退出等待
wait和sleep區別
wait/notify正確用法
1.如果只有一個線程wait,等待另一個線程將條件滿足后notify即可
2.如果有多個線程wait,還用notify可能會出現虛假喚醒,可以改用notifyAll喚醒全部;此時肯定喚醒了其他不必要的線程,可以使用循環條件判斷 while 讓他們再次進入wait等待下次喚醒。
同步模式之保護性暫停
定義
代碼實現
1.使用join后線程不能執行其他操作,而保護性暫停可以在等待過程中執行其他操作
2.使用join獲得的結果的變量是全局的,而保護性暫停的變量是局部的
@Slf4j(topic = "c.Test15")
public class Test15 {public static void main(String[] args) throws InterruptedException {GuardedObject guardedObject = new GuardedObject();Thread t1, t2;t1 = new Thread(() -> {log.debug("等待結果..");String result = (String) guardedObject.get();log.debug("結果是:{}", result);}, "t1");t2 = new Thread(() -> {log.debug("進行下載..");// 等待下載完成結果try {sleep(1000);String result = "下載完成";guardedObject.complete(result);} catch (InterruptedException e) {throw new RuntimeException(e);}}, "t2");t1.start();t2.start();}
}class GuardedObject {private Object response;public Object get() {synchronized (this) {while (response == null) {try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}return response;}}public void complete(Object response) {synchronized (this) {this.response = response;this.notifyAll();}}}
擴展--增加超時等待
1.記錄等待時間和經歷時間:超時時間 - 經歷時間 = 等待時間
2.防止虛假喚醒對等待時間的影響
join原理
join源碼使用的就是保護性暫停模式
擴展--解耦中間類
產生結果線程和消費結果線程一一對應
收信人通知信箱要收信,并準備接收;
信箱創建郵件并附上唯一的id,每創建一份就把郵件放到集合里;
快遞員拿到郵件id和郵件內容開始送信,信件送達后通知收信人。
@Slf4j(topic = "c.Test16")
public class Test16 {public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 3; i++) {new People().start();}sleep(1000);for (Integer id : Mailboxes.getIds()) {new Postman(id, "mail" + id).start();}}
}
@Slf4j(topic = "c.People")
class People extends Thread {@Overridepublic void run() {//收信GauardedObject gauardObject = Mailboxes.createGauardedObject();log.debug("開始收信 id:{}", gauardObject.getId());Object mail = gauardObject.get(5000);log.debug("收到信 id:{} mail:{}", gauardObject.getId(), mail);}
}@Slf4j(topic = "c.Postman")
class Postman extends Thread {private int id;private String mail;public Postman(int id, String mail) {this.id = id;this.mail = mail;}@Overridepublic void run() {GauardedObject gauardedObject = Mailboxes.getGauardedObject(id);log.debug("開始送信 id:{} mail:{}", id, mail);gauardedObject.complete(mail);}
}class Mailboxes {private static Map<Integer, GauardedObject> boxes = new Hashtable<>();private static int id = 1;private static synchronized int generateId() {return id++;}public static GauardedObject getGauardedObject(int id) {return boxes.remove(id);}public static GauardedObject createGauardedObject() {GauardedObject go = new GauardedObject(generateId());boxes.put(go.getId(), go);return go;}public static Set<Integer> getIds() {return boxes.keySet();}}class GauardedObject {private int id;public GauardedObject(int id) {this.id = id;}public int getId() {return id;}private Object response;public Object get(long timeout) {synchronized (this) {long passedtime = 0;long start = System.currentTimeMillis();while (response == null) {long waitTime = timeout - passedtime;if (waitTime < 0){break;}try {this.wait(waitTime);} catch (InterruptedException e) {e.printStackTrace();}passedtime = System.currentTimeMillis() - start;}return response;}}public void complete(Object response) {synchronized (this) {this.response = response;this.notifyAll();}}
}
異步模式之生產者/消費者
定義
存放在消息隊列里的消息不會立刻被消費,所以歸類為異步。
注:這個消息隊列是線程間通信的,而RabbitMQ里消息隊列是進程間通信的
消息隊列里要有容量限制
填充隊列時要檢查是否容量已滿
拿去消息時要檢查隊列消息是否為空
代碼實現
@Slf4j(topic = "c.Test15")
public class Test17 {public static void main(String[] args) throws InterruptedException {MessageQueue queue = new MessageQueue(2);for (int i = 0; i < 3; i++) {int id = i;new Thread(() -> {queue.put(new Message(id, "msg" + id));log.debug("id:{},生產消息:{}", id, "msg" + id);}, "生產者" + i).start();}sleep(1000);new Thread(() -> {while (true) {Message message = queue.take();log.debug("id:{},消費消息:{}", message.getId(), message.getValue());}}).start();}
}@Slf4j(topic = "c.MessageQueue")
class MessageQueue {private int capacity;private LinkedList<Message> queue = new LinkedList<>();public MessageQueue(int capacity) {this.capacity = capacity;}public Message take() {synchronized (this) {while (queue.isEmpty()) {log.debug("隊列為空,等待消息填充!");try {this.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}this.notifyAll();return queue.removeFirst();}}public void put(Message message) {synchronized (this) {while (queue.size() == capacity) {log.debug("隊列已滿,等待消費!");try {this.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}queue.addLast(message);this.notifyAll();}}
}final class Message {private int id;private Object value;public Message(int id, Object value) {this.id = id;this.value = value;}public int getId() {return id;}public Object getValue() {return value;}
}
park&unpark
與wait¬ify區別
原理
線程狀態轉換
單向箭頭表示只能一種狀態向另一種狀態轉換
雙向箭頭表示可以互相轉換
多把鎖
如圖所示,房間可以睡覺和學習,此時如果有人要睡覺,會把屋子鎖上,而要進屋學習的人就必須等待睡覺結束,導致并發度很低。顯然兩者不沖突,需要優化。
用多把鎖優化(保證業務不關聯)
將鎖的粒度細分
好處,是可以增強并發度
壞處,如果一個線程需要同時獲得多把鎖,就容易發生死鎖
?
線程活躍性
死鎖
死鎖定位
方法一:jstack
利用? jps 定位線程id
使用? "jstack?線程id" 命令查看
方法二:jconsole
命令行輸入 jconsole 打開
連接到所在線程
切換線程窗口,檢測死鎖
哲學家就餐
問題原因:syschronized獲取不到鎖會一直等待。
public class Test21 {public static void main(String[] args) throws InterruptedException {Chopstick c1 = new Chopstick("C1");Chopstick c2 = new Chopstick("C2");Chopstick c3 = new Chopstick("C3");Chopstick c4 = new Chopstick("C4");Chopstick c5 = new Chopstick("C5");new philosopher("p1", c1, c2).start();new philosopher("p2", c2, c3).start();new philosopher("p3", c3, c4).start();new philosopher("p4", c4, c5).start();new philosopher("p5", c5, c1).start();}
}@Slf4j
class philosopher extends Thread{Chopstick left;Chopstick right;public philosopher(String name, Chopstick left, Chopstick right){super(name);this.left = left;this.right = right;}@Overridepublic void run() {while (true) {synchronized (left) {synchronized (right) {eat();}}}}private void eat() {log.debug("eating....");try {sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}class Chopstick{String name;public Chopstick(String name){this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
活鎖
解決活鎖方法:讓兩個線程指令交錯執行,下圖的活鎖問題可以通過增加一個線程的睡眠時間解決
饑餓
死鎖問題是兩個線程要獲取對方擁有的鎖
順序加鎖是讓兩個線程都先獲取A鎖,必然有一個線程獲取不到鎖導致阻塞,另一個線程就能再次獲取到B鎖
造成問題:有的鎖可能會一直得不到鎖,導致無法得到執行。
ReentrantLock
相對于synchronized它具備如下特點
可中斷(syschronized鎖加上后不可被其他方法中斷破壞)
可以設置超時時間(syschronized的阻塞線程會進入EntryList里一直等待,而RL可以設置等待時間,超過時間會放棄鎖競爭)
可以設置為公平鎖(防止饑餓鎖問題)
支持多個條件變量(多個細分的休息室)
與synchronized一樣,都支持可重入(同一線程可以對同一對象反復加鎖)
可重入鎖
@Slf4j(topic = "c.Test19")
public class Test19 {private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {lock.lock();try {log.debug("main");m1();} finally {lock.unlock();}}private static void m1() {lock.lock();try {log.debug("m1");m2();} finally {lock.unlock();}}private static void m2() {lock.lock();try {log.debug("m2");} finally {lock.unlock();}}
}
可打斷
lock方法不支持打斷
加入打斷機制,防止無限制的運行,避免產生死鎖
其他線程調用interrupt打斷無限制的等待
public class Test20 {private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {try {log.debug("嘗試獲取鎖");lock.lockInterruptibly();} catch (InterruptedException e) {log.debug("沒有獲取到鎖");e.printStackTrace();return;}try {log.debug("獲取到鎖");} finally {lock.unlock();}}, "t1");lock.lock();t1.start();log.debug("開啟打斷");t1.interrupt();}
}
鎖超時
主動避免無限制等待的手段
超過等待時間,主動放棄獲取鎖,避免死鎖
加入等待時間,且tryLock也支持打斷
解決哲學家就餐問題
將Chopstick的對象做為一個ReentrantLock的鎖對象
在獲取不到鎖時主動釋放鎖,從而避免了每個人都拿一個筷子的死鎖問題
public class Test21 {public static void main(String[] args) throws InterruptedException {Chopstick c1 = new Chopstick("C1");Chopstick c2 = new Chopstick("C2");Chopstick c3 = new Chopstick("C3");Chopstick c4 = new Chopstick("C4");Chopstick c5 = new Chopstick("C5");new philosopher("p1", c1, c2).start();new philosopher("p2", c2, c3).start();new philosopher("p3", c3, c4).start();new philosopher("p4", c4, c5).start();new philosopher("p5", c5, c1).start();}
}@Slf4j
class philosopher extends Thread{Chopstick left;Chopstick right;public philosopher(String name, Chopstick left, Chopstick right){super(name);this.left = left;this.right = right;}@Overridepublic void run() {while (true) {if (left.tryLock()) {try {if (right.tryLock()) {try {eat();} finally {right.unlock();}}} finally {left.unlock();}}}}private void eat() {log.debug("eating....");try {sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}class Chopstick extends ReentrantLock{String name;public Chopstick(String name){this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
公平鎖
不公平鎖:在鎖釋放時,處于阻塞隊列的線程搶鎖,誰搶到誰執行,不按照在阻塞隊列的順序先入先得獲得鎖
ReentrantLock默認時不公平的
條件變量
@Slf4j(topic = "c.Test22")
public class Test22 {static boolean hasCigarette = false;static boolean hasTakeout = false;static ReentrantLock ROOM = new ReentrantLock();static Condition waitCigaretteSet = ROOM.newCondition();static Condition waitTakeoutSet = ROOM.newCondition();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {ROOM.lock();try {log.debug("有煙沒?");while (!hasCigarette) {log.debug("沒有煙,先去休息!");try {waitCigaretteSet.await();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("可以開始干活了");} finally {ROOM.unlock();}}, "t1");Thread t2 = new Thread(() -> {ROOM.lock();try {log.debug("外賣到了沒?");while (!hasTakeout) {log.debug("沒有外賣,先去休息!");try {waitTakeoutSet.await();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("有外賣了,開始干活!");} finally {ROOM.unlock();}}, "t2");t1.start();t2.start();Thread.sleep(1000);new Thread(() -> {ROOM.lock();try {log.debug("煙到了!");hasCigarette = true;waitCigaretteSet.signal();} finally {ROOM.unlock();}}, "送煙的").start();new Thread(() -> {ROOM.lock();try {log.debug("外賣到了!");hasTakeout = true;waitTakeoutSet.signal();} finally {ROOM.unlock();}}, "送外賣的").start();}
}
同步模式之順序控制(先2后1)
固定運行順序--wait notify
用一個變量記錄t2是否執行,t1根據變量來決定是否要等待。
@Slf4j(topic = "c.Test23")
public class Test23 {static Object lock = new Object();static boolean t2Runned = false;public static void main(String[] args) throws InterruptedException {new Thread(() -> {synchronized (lock) {while (!t2Runned) {try {lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}log.debug("t1");}}, "t1").start();new Thread(() -> {synchronized (lock) {log.debug("t2");t2Runned = true;lock.notifyAll();}}, "t2").start();}
}
固定運行順序--await signal
@Slf4j(topic = "c.Test23")
public class Test23 {static ReentrantLock lock = new ReentrantLock();static boolean t2Runned = false;static Condition t1Room = lock.newCondition();public static void main(String[] args) throws InterruptedException {new Thread(() -> {lock.lock();try {while (!t2Runned) {try {t1Room.await();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("t1");} finally {lock.unlock();}}, "t1").start();new Thread(() -> {lock.lock();try {log.debug("t2");t2Runned = true;t1Room.signal();} finally {lock.unlock();}}, "t2").start();}
}
固定運行順序--park unpark
@Slf4j(topic = "c.Test24")
public class Test24 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{LockSupport.park();log.debug("t1");},"t1");Thread t2 = new Thread(()->{log.debug("t2");LockSupport.unpark(t1);},"t2");t1.start();t2.start();}
}
交替輸出(1輸出a5次,2輸出b5次,3輸出c5次)
交替輸出--wait notify
@Slf4j(topic = "c.Test25")
public class Test25 {public static void main(String[] args) throws InterruptedException {WaitNotify waitNotify = new WaitNotify(1, 5);new Thread(() -> {waitNotify.print("a", 1, 2);}, "t1").start();new Thread(() -> {waitNotify.print("b", 2, 3);}, "t2").start();new Thread(() -> {waitNotify.print("c", 3, 1);}, "t3").start();}
}class WaitNotify {private int flag;private int loopNum;public WaitNotify(int flag, int loopNum) {this.flag = flag;this.loopNum = loopNum;}public void print(String name, int waitFlag, int nextFlag) {for (int i = 0; i < loopNum; i++) {synchronized (this) {while (flag != waitFlag) {try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.print(name);flag = nextFlag;this.notifyAll();};}}
}
交替輸出--await signal
@Slf4j(topic = "c.Test27")
public class Test27 {static ReentrantLock lock = new ReentrantLock();static Condition t1Room = lock.newCondition();static Condition t2Room = lock.newCondition();static Condition t3Room = lock.newCondition();public static void main(String[] args) throws InterruptedException {AwaitSignal awaitSignal = new AwaitSignal(5, lock);new Thread(() -> {awaitSignal.print("a", t1Room, t2Room);}, "t1").start();new Thread(() -> {awaitSignal.print("b", t2Room, t3Room);}, "t2").start();new Thread(() -> {awaitSignal.print("c", t3Room, t1Room);}, "t3").start();Thread.sleep(1000);lock.lock();try {t1Room.signal();} finally {lock.unlock();}}
}class AwaitSignal {private int loopNum;private ReentrantLock lock;public AwaitSignal(int loopNum, ReentrantLock lock) {this.loopNum = loopNum;this.lock = lock;}public void print(String name, Condition currentRoom, Condition nextRoom) {for (int i = 0; i < loopNum; i++) {lock.lock();try {currentRoom.await();System.out.print(name);nextRoom.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}}
}
交替輸出--park unpark
@Slf4j(topic = "c.Test26")
public class Test26 {static Thread t1, t2, t3;public static void main(String[] args) throws InterruptedException {t1 = new Thread(() -> {for (int i = 0; i < 5; i++) {LockSupport.park();System.out.print("a");LockSupport.unpark(t2);}}, "t1");t2 = new Thread(() -> {for (int i = 0; i < 5; i++) {LockSupport.park();System.out.print("b");LockSupport.unpark(t3);}}, "t2");t3 = new Thread(() -> {for (int i = 0; i < 5; i++) {LockSupport.park();System.out.print("c");LockSupport.unpark(t1);}}, "t3");t1.start();t2.start();t3.start();LockSupport.unpark(t1);}
}