Java并发编程之美读书笔记-并发编程基础2
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
1.線程的通知與等待
Java中的Object類(lèi)是所有類(lèi)的父親,鑒于繼承機(jī)制,Java把所有類(lèi)都需要的方法放到了Object類(lèi)里面,其中就包含線程的通知與等待系列。
wait()方法
當(dāng)一個(gè)線程調(diào)用一個(gè)共享變量的wait()方法時(shí),該調(diào)用線程會(huì)被阻塞掛起,并釋放該共享變量上的鎖,直到下面幾件事情之一才返回:
1)其他線程調(diào)用了該共享對(duì)象的notify()或者notifyAll()方法
2)其他線程調(diào)用了該線程的interrupt()方法,該線程拋出InterruptedException異常返回
另外需要注意的是調(diào)用共享變量的wait()方法前需要事先獲取該對(duì)象的監(jiān)視器鎖,否則調(diào)用 wait()方法時(shí)調(diào)用線程會(huì)拋出IllegalMonitorStateException異常。
如何獲取監(jiān)視器鎖?
a.執(zhí)行synchronized同步代碼塊時(shí),使用該共享變量作為參數(shù)
synchronized(共享變量) {// doSomething }b.調(diào)用該共享變量的方法,并且該方法使用了synchronized修飾
synchronized void add(int a, int b) {// doSomething }需要注意的是一個(gè)線程可以從阻塞掛起狀態(tài)變?yōu)榭蛇\(yùn)行狀態(tài)(也就是被喚醒),即使該線程沒(méi)有被其他線程調(diào)用notify()、notifyAll()方法進(jìn)行通知,或者被中斷,或者等待超時(shí),這就是所謂的虛假喚醒。
防范虛假喚醒的一個(gè)方法就是在一個(gè)循環(huán)中調(diào)用wait()方法,退出循環(huán)的條件是滿足了喚醒該線程的條件。
synchronized(obj) {while(條件不滿足) {obj.wait();} }wait(long timeout)方法
與wait()方法的不同之處在于,如果一個(gè)線程調(diào)用共享對(duì)象的該方法掛起后,沒(méi)有指定的timeout ms時(shí)間內(nèi)被其他線程調(diào)用該共享變量的notify()或者notifyAll()方法喚醒,那么該函數(shù)還是會(huì)因?yàn)槌瑫r(shí)返回。
notify()方法
一個(gè)線程調(diào)用共享對(duì)象的notify()方法后,會(huì)喚醒一個(gè)在該變量上調(diào)用wait系列方法后被掛起的線程。一個(gè)共享變量上可能會(huì)有多個(gè)線程在等待,具體喚醒哪個(gè)等待的線程是隨機(jī)的。
此外,被喚醒的線程不能馬上從wait()方法返回并繼續(xù)執(zhí)行,它必須在獲取了共享對(duì)象的監(jiān)視器鎖后才可以返回,也就是被喚醒的線程也不一定會(huì)獲取到共享變量的監(jiān)視器鎖,這是因?yàn)樵摼€程要和其他線程一起競(jìng)爭(zhēng)該鎖,只有競(jìng)爭(zhēng)到了共享變量的監(jiān)視器鎖后才可以繼續(xù)執(zhí)行。
類(lèi)似wait方法,只有當(dāng)前線程獲取到監(jiān)視器鎖后才可以調(diào)用notify()方法,否則會(huì)拋出IllegalMonitorStateException異常。
notifyAll()方法
與notify()方法的區(qū)別是,notify()會(huì)喚醒一個(gè)阻塞在該共享變量上的一個(gè)線程,notifyAll()方法則會(huì)喚醒所有在共享變量上的=由于被調(diào)用wait()方法而被掛起的線程。
一個(gè)生產(chǎn)者消費(fèi)者的例子:
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.等待線程執(zhí)行終止
在項(xiàng)目實(shí)踐中經(jīng)常會(huì)遇到一個(gè)場(chǎng)景就是需要等待某幾件事情完成后才能繼續(xù)往下執(zhí)行,比如多個(gè)線程加載資源,需要等待多個(gè)線程全部加載完畢再匯總處理。Thread類(lèi)中有一個(gè)join方法提供該功能。
看一個(gè)栗子:
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調(diào)用線程B的join方法后會(huì)被阻塞,當(dāng)其他線程調(diào)用了線程A的interrupt()方法中斷了線程A時(shí),線程A會(huì)拋出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();}} }注意這里調(diào)用的是主線程的interrupted方法。
轉(zhuǎn)載于:https://my.oschina.net/hensemlee/blog/3003562
總結(jié)
以上是生活随笔為你收集整理的Java并发编程之美读书笔记-并发编程基础2的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 易天光通信ETU 25G SFP28光模
- 下一篇: Java B2B2C多用户商城 spri