Java并发编程之美读书笔记-并发编程基础2
2019獨角獸企業重金招聘Python工程師標準>>>
1.線程的通知與等待
Java中的Object類是所有類的父親,鑒于繼承機制,Java把所有類都需要的方法放到了Object類里面,其中就包含線程的通知與等待系列。
wait()方法
當一個線程調用一個共享變量的wait()方法時,該調用線程會被阻塞掛起,并釋放該共享變量上的鎖,直到下面幾件事情之一才返回:
1)其他線程調用了該共享對象的notify()或者notifyAll()方法
2)其他線程調用了該線程的interrupt()方法,該線程拋出InterruptedException異常返回
另外需要注意的是調用共享變量的wait()方法前需要事先獲取該對象的監視器鎖,否則調用 wait()方法時調用線程會拋出IllegalMonitorStateException異常。
如何獲取監視器鎖?
a.執行synchronized同步代碼塊時,使用該共享變量作為參數
synchronized(共享變量) {// doSomething }b.調用該共享變量的方法,并且該方法使用了synchronized修飾
synchronized void add(int a, int b) {// doSomething }需要注意的是一個線程可以從阻塞掛起狀態變為可運行狀態(也就是被喚醒),即使該線程沒有被其他線程調用notify()、notifyAll()方法進行通知,或者被中斷,或者等待超時,這就是所謂的虛假喚醒。
防范虛假喚醒的一個方法就是在一個循環中調用wait()方法,退出循環的條件是滿足了喚醒該線程的條件。
synchronized(obj) {while(條件不滿足) {obj.wait();} }wait(long timeout)方法
與wait()方法的不同之處在于,如果一個線程調用共享對象的該方法掛起后,沒有指定的timeout ms時間內被其他線程調用該共享變量的notify()或者notifyAll()方法喚醒,那么該函數還是會因為超時返回。
notify()方法
一個線程調用共享對象的notify()方法后,會喚醒一個在該變量上調用wait系列方法后被掛起的線程。一個共享變量上可能會有多個線程在等待,具體喚醒哪個等待的線程是隨機的。
此外,被喚醒的線程不能馬上從wait()方法返回并繼續執行,它必須在獲取了共享對象的監視器鎖后才可以返回,也就是被喚醒的線程也不一定會獲取到共享變量的監視器鎖,這是因為該線程要和其他線程一起競爭該鎖,只有競爭到了共享變量的監視器鎖后才可以繼續執行。
類似wait方法,只有當前線程獲取到監視器鎖后才可以調用notify()方法,否則會拋出IllegalMonitorStateException異常。
notifyAll()方法
與notify()方法的區別是,notify()會喚醒一個阻塞在該共享變量上的一個線程,notifyAll()方法則會喚醒所有在共享變量上的=由于被調用wait()方法而被掛起的線程。
一個生產者消費者的例子:
public class WaitNotifyTest3 {private static final Queue<String> queue = new LinkedBlockingQueue<>();private static final int MAX_SIZE = 1000;public static void main(String[] args) {Thread producer = new Thread(() -> {while (true) {synchronized (queue) {while(queue.size() == MAX_SIZE) {try {queue.wait();} catch(Exception ex) {ex.printStackTrace();}}queue.add("ele");queue.notify();}}});Thread consumer = new Thread(() -> {while (true) {synchronized (queue) {while(queue.size() == 0) {try {queue.wait();} catch(Exception ex) {ex.printStackTrace();}}System.out.println(queue.poll());queue.notify();}}});producer.start();consumer.start();} }2.等待線程執行終止
在項目實踐中經常會遇到一個場景就是需要等待某幾件事情完成后才能繼續往下執行,比如多個線程加載資源,需要等待多個線程全部加載完畢再匯總處理。Thread類中有一個join方法提供該功能。
看一個栗子:
public class JoinTest {public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(() -> {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("child threadOne over");});Thread thread2 = new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}try {thread1.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("child threadTwo over");});thread1.start();thread2.start();System.out.println("wait all child thread over");thread1.join();thread2.join();System.out.println("all child thread over");} }另外線程A調用線程B的join方法后會被阻塞,當其他線程調用了線程A的interrupt()方法中斷了線程A時,線程A會拋出InterruptedException異常而返回。如下:
public class JoinTest2 {public static void main(String[] args) {Thread thread1 = new Thread(() -> {System.out.println("thread1 begin run");for (; ; ) {}});final Thread threadMain = Thread.currentThread();Thread thread2 = new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}threadMain.interrupt();});thread1.start();thread2.start();try {thread1.join();} catch (InterruptedException e) {e.printStackTrace();}} }注意這里調用的是主線程的interrupted方法。
轉載于:https://my.oschina.net/hensemlee/blog/3003562
總結
以上是生活随笔為你收集整理的Java并发编程之美读书笔记-并发编程基础2的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 易天光通信ETU 25G SFP28光模
- 下一篇: 乱入Linux界的我是如何学习的