JUC并發編程(上)

一、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&notify區別

原理

線程狀態轉換

單向箭頭表示只能一種狀態向另一種狀態轉換

雙向箭頭表示可以互相轉換

多把鎖

如圖所示,房間可以睡覺和學習,此時如果有人要睡覺,會把屋子鎖上,而要進屋學習的人就必須等待睡覺結束,導致并發度很低。顯然兩者不沖突,需要優化。

用多把鎖優化(保證業務不關聯)

將鎖的粒度細分
好處,是可以增強并發度
壞處,如果一個線程需要同時獲得多把鎖,就容易發生死鎖

?

線程活躍性

死鎖

死鎖定位
方法一: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);}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/79414.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/79414.shtml
英文地址,請注明出處:http://en.pswp.cn/web/79414.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

單地平面6層PCB設計實戰:如何兼顧電源與信號完整性?

摘要&#xff1a;面對復雜系統&#xff08;SDRAM、WiFi、電機驅動等&#xff09;且僅有1層地平面的6層板設計挑戰&#xff0c;本文從層疊規劃、電源噪聲抑制、高速信號處理等角度&#xff0c;總結可落地的設計技巧與避坑指南。 一、層疊設計&#xff1a;6層板如何“擠”出最優布…

spark:map 和 flatMap 的區別(Scala)

場景設定 假設有一個包含句子的 RDD&#xff1a; scala val rdd sc.parallelize(List("Hello World", "Hi Spark")) 目標是&#xff1a;將每個句子拆分成單詞。 1. 用 map 的效果 代碼示例 scala val resultMap rdd.map(sentence > sentence…

基于VSCode+PlatformIO環境的ESP8266的HX1838紅外模塊

以下是針對ESP8266開發板的紅外遙控解碼系統開發教程&#xff0c;基于VSCodePlatformIO環境編寫 一、概述 本實驗通過ESP8266開發板實現&#xff1a; 紅外遙控信號解碼自定義按鍵功能映射串口監控輸出基礎設備控制&#xff08;LED&#xff09; 硬件組成&#xff1a; NodeMC…

Kubernetes排錯(十四):Pod狀態異常排查手冊

當你在凌晨三點收到告警&#xff0c;發現Pod在崩潰循環中掙扎時&#xff0c;如何快速定位問題&#xff1f;本文將為你梳理一套生產環境通用的Pod排錯流程&#xff0c;并附上救火隊員必備的實用命令清單&#xff01; 一、5分鐘快速定位&#xff1a;四步鎖定問題方向 步驟1&…

醫院藥品管理系統(準備工作)

準備工作 創建數據庫表 搭建Springboot框架 創建工程 定位maven 其他準備工作 創建數據庫表 建了九張表 搭建Springboot框架 創建工程 定位maven 把鏡像改為國內的 其他準備工作 安裝Lombok插件 額外添加依賴 如果添加依賴的過程中一直爆紅&#xff0c;可以刷新…

SpringBoot異步處理@Async深度解析:從基礎到高階實戰

一、異步編程基礎概念 1.1 同步 vs 異步 特性同步異步執行方式順序執行&#xff0c;阻塞調用非阻塞&#xff0c;調用后立即返回線程使用單線程完成所有任務多線程并行處理響應性較差&#xff0c;需等待前任務完成較好&#xff0c;可立即響應新請求復雜度簡單直觀較復雜&#…

簡單的強化學習舉例

1&#xff0c;定義獎勵函數 首先&#xff0c;需要根據具體的任務需求來定義獎勵函數。例如&#xff0c;對于機器人導航任務&#xff0c;可以根據機器人與目標點的距離來定義獎勵函數&#xff1a; import numpy as npdef navigation_reward(robot_position, target_position):…

css背景相關

背景書寫 background: url(src); // 注意&#xff1a;在寫動態樣式時&#xff0c;backgournd賦值格式錯誤&#xff0c;是不會在瀏覽器dom的style上顯示的 // 但是可以創建不可見的img&#xff0c;預加載來提高性能背景也會加載圖片資源 同img的src一樣&#xff0c;background也…

opencascade.js stp vite 調試筆記

Hello, World! | Op enCascade.js cnpm install opencascade.js cnpm install vite-plugin-wasm --save-dev 當你不知道文件寫哪的時候trae還是有點用的 ‘’‘ import { defineConfig } from vite; import wasm from vite-plugin-wasm; import rollupWasm from rollup/plug…

線程的一些事(2)

在java中&#xff0c;線程的終止&#xff0c;是一種“軟性”操作&#xff0c;必須要對應的線程配合&#xff0c;才能把終止落實下去 然而&#xff0c;系統原生的api其實還提供了&#xff0c;強制終止線程的操作&#xff0c;無論線程執行到哪&#xff0c;都能強行把這個線程干掉…

BGP實驗練習1

需求&#xff1a; 要求五臺路由器的環回地址均可以相互訪問 需求分析&#xff1a; 1.圖中存在五個路由器 AR1、AR2、AR3、AR4、AR5&#xff0c;分屬不同自治系統&#xff08;AS&#xff09;&#xff0c;AR1 在 AS 100&#xff0c;AR2 - AR4 在 AS 200&#xff0c;AR5 在 AS …

滑動窗口——將x減到0的最小操作數

題目&#xff1a; 這個題如果我們直接去思考方法是很困難的&#xff0c;因為我們不知道下一步是在數組的左還是右操作才能使其最小。正難則反&#xff0c;思考一下&#xff0c;無論是怎么樣的&#xff0c;最終這個數組都會分成三個部分左中右&#xff0c;而左右的組合就是我們…

C++ RAII機制

RAII&#xff08;Resource Acquisition Is Initialization&#xff09;是一種編程范式&#xff0c;核心思想是&#xff1a;資源的生命周期與對象綁定——對象創建時獲取資源&#xff0c;對象銷毀時自動釋放資源。這種機制通過構造函數和析構函數的配對執行&#xff0c;確保資源…

連續抵消解碼器--Successive Cancellation decoder(SC 解碼器)

在這里&#xff0c;我們來看一下&#xff08;Arikan&#xff0c;2009&#xff09;中提供的連續取消解碼算法。 顧名思義&#xff0c;SC解碼算法從u0開始按順序解碼比特。 凍結的比特節點總是被解碼為0。 在解碼ui時&#xff0c;根據以下規則使用由向量表示的可用比特來解碼u…

suricata之規則去重

一、環境和背景 1.1 環境 OS: Ubuntu 22.04.5 LTS IDE: vscode suricata: suricata 7.0.5 1.2 背景 在添加規則時&#xff0c;為了給規則分類&#xff0c;將不同類別的規則寫入不同的文件。 在規則加載時兩條不同的規則卻被認為是重復的&#xff0c;因此記錄一下去重邏輯。…

vue vite 無法熱更新問題

一、在vue頁面引入組件CustomEmployeesDialog&#xff0c;修改組件CustomEmployeesDialog無法熱更新 引入方式&#xff1a; import CustomEmployeesDialog from ../dialog/customEmployeesDialog.vue 目錄結構&#xff1a; 最后發現是引入import時&#xff0c;路徑大小寫與目…

深入理解 Linux 權限控制機制

引言 在 Linux 系統中&#xff0c;權限控制是保障系統安全的核心機制。通過限制用戶對文件和資源的訪問&#xff0c;它能有效防止未授權操作&#xff0c;保護數據不被篡改或泄露。合理設置權限不僅有助于實現用戶隔離和最小權限原則&#xff0c;還能降低系統被濫用或攻擊的風險…

Oracle版本、補丁及升級(12)——版本體系

12.1. 版本體系 Oracle作為最流行的一款關系數據庫軟件產品,其擁有自己一套成熟的版本管理體系。具體版本體系以12c為分界線,前后版本體系分別不同。 ???????12.1.1. 12c之前版本 12c之前的Oracle,版本共有5位阿拉伯數字組成,其中的每位數字,都有各自的含義,具…

Maven 中的 pom.xml 文件

目錄標題 1、根標簽 <project> 的直接子標簽順序?2、常見子標簽內部順序?2.1、<build> 標簽內部順序2.2、<dependencies> 標簽內部順序 3、modelVersion 為什么是 4.0.0 &#xff1f;4、<parent> 標簽&#xff1f;??4.1、為什么需要 <parent>…

方案精讀:華為與中軟-智慧園區解決方案技術主打膠片【附全文閱讀】

該文檔是華為與中軟國際的智慧園區輕量化解決方案推介&#xff0c;主要面向園區管理者、運營者及相關企業決策人。其核心圍繞園區痛點&#xff0c;闡述智慧園區的發展趨勢。 方案涵蓋綜合安防、便捷通行、設備管理等多領域應用場景&#xff0c;通過智能視頻監控、人臉識別、遠程…