摘要:和簡介和均為的方法暫停一個(gè)線程喚醒一個(gè)線程從以上的定義中,我們可以了解到以下事實(shí)想要使用這兩個(gè)方法,我們需要先有一個(gè)對象。在中任何一個(gè)時(shí)刻,對象的控制權(quán)只能被一個(gè)線程擁有。若有多個(gè)線程處于此控制權(quán)下的狀態(tài),只有一個(gè)會(huì)被喚醒。
最近看帖子,發(fā)現(xiàn)一道面試題:
啟動(dòng)兩個(gè)線程, 一個(gè)輸出 1,3,5,7…99, 另一個(gè)輸出 2,4,6,8…100 最后 STDOUT 中按序輸出 1,2,3,4,5…100
題目要求用 Java 的 wait + notify 機(jī)制來實(shí)現(xiàn),重點(diǎn)考察對于多線程可見性的理解。
wait 和 notify 簡介wait 和 notify 均為 Object 的方法:
Object.wait() —— 暫停一個(gè)線程
Object.notify() —— 喚醒一個(gè)線程
從以上的定義中,我們可以了解到以下事實(shí):
想要使用這兩個(gè)方法,我們需要先有一個(gè)對象 Object。
在多個(gè)線程之間,我們可以通過調(diào)用同一個(gè)對象的wait()和notify()來實(shí)現(xiàn)不同的線程間的可見。
對象控制權(quán)(monitor)在使用 wait 和 notify 之前,我們需要先了解對象的控制權(quán)(monitor)。在 Java 中任何一個(gè)時(shí)刻,對象的控制權(quán)只能被一個(gè)線程擁有。如何理解控制權(quán)呢?請先看下面的簡單代碼:
public class ThreadTest { public static void main(String[] args) { Object object = new Object(); new Thread(new Runnable() { @Override public void run() { try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } }
直接執(zhí)行,我們將會(huì)得到以下異常:
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:502) at com.xiangyu.demo.ThreadTest$1.run(ThreadTest.java:10) at java.lang.Thread.run(Thread.java:748)
出錯(cuò)的代碼在:object.wait();。這里我們需要了解以下事實(shí):
無論是執(zhí)行對象的 wait、notify 還是 notifyAll 方法,必須保證當(dāng)前運(yùn)行的線程取得了該對象的控制權(quán)(monitor)
如果在沒有控制權(quán)的線程里執(zhí)行對象的以上三種方法,就會(huì)報(bào) java.lang.IllegalMonitorStateException 異常。
JVM 基于多線程,默認(rèn)情況下不能保證運(yùn)行時(shí)線程的時(shí)序性
在上面的示例代碼中,我們 new 了一個(gè) Thread,但是對象 object 的控制權(quán)仍在主線程里。所以會(huì)報(bào) java.lang.IllegalMonitorStateException 。
我們可以通過同步鎖來獲得對象控制權(quán),例如:synchronized 代碼塊。對以上的示例代碼做改造:
public class ThreadTest { public static void main(String[] args) { Object object = new Object(); new Thread(new Runnable() { @Override public void run() { synchronized (object){ // 修改處 try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } }
再次執(zhí)行,代碼不再報(bào)錯(cuò)。
我們可以得到以下結(jié)論:
調(diào)用對象的wait()和notify()方法,需要先取得對象的控制權(quán)
可以使用synchronized (object)來取得對于 object 對象的控制權(quán)
解題了解了對象控制權(quán)之后,我們就可以正常地使用 notify 和 wait 了,下面給出我的解題方法,供參考。
public class ThreadTest { private final Object flag = new Object(); public static void main(String[] args) { ThreadTest threadTest = new ThreadTest(); ThreadA threadA = threadTest.new ThreadA(); threadA.start(); ThreadB threadB = threadTest.new ThreadB(); threadB.start(); } class ThreadA extends Thread { @Override public void run() { synchronized (flag) { for (int i = 0; i <= 100; i += 2) { flag.notify(); System.out.println(i); try { flag.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } class ThreadB extends Thread { @Override public void run() { synchronized (flag) { for (int i = 1; i < 100; i += 2) { flag.notify(); System.out.println(i); try { flag.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }發(fā)散:notify()和notifyAll()
這兩個(gè)方法均為 native 方法,在JDK 1.8 中的關(guān)于notify()的JavaDoc如下:
Wakes up a single thread that is waiting on this object"s monitor. If any threads are waiting on this object, one of them is chosen to be awakened.
譯為:
喚醒此 object 控制權(quán)下的一個(gè)處于 wait 狀態(tài)的線程。若有多個(gè)線程處于此 object 控制權(quán)下的 wait 狀態(tài),只有一個(gè)會(huì)被喚醒。
也就是說,如果有多個(gè)線程在 wait 狀態(tài),我們并不知道哪個(gè)線程會(huì)被喚醒。
在JDK 1.8 中的關(guān)于notifyAll()的JavaDoc如下:
Wakes up all threads that are waiting on this object"s monitor.
譯為:
喚醒所有處于此 object 控制權(quán)下的 wait 狀態(tài)的線程。
所以,我們需要根據(jù)實(shí)際的業(yè)務(wù)場景來考慮如何使用。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/77389.html
摘要:不釋放持有的鎖,釋放鎖。在調(diào)用方法前,必須持有鎖,調(diào)用喚醒,也要持有鎖。休眠一定時(shí)間后,進(jìn)入就緒狀態(tài)。這兩個(gè)都能被方法中斷當(dāng)前狀態(tài)。用法方獲取鎖判斷條件,不滿足繼續(xù)滿足執(zhí)行其他業(yè)務(wù)方獲取鎖改變條件通知為什么是而不是會(huì)一直循環(huán),直到條件滿足。 sleep和wait sleep是Thread類的方法,wait是Object的方法。 sleep可以到處使用,wait必須是在同步方法或者代碼...
摘要:用線程表示維修的過程維修結(jié)束把廁所置為可用狀態(tài)維修工把廁所修好了,準(zhǔn)備釋放鎖了這個(gè)維修計(jì)劃的內(nèi)容就是當(dāng)維修工進(jìn)入廁所之后,先把門鎖上,然后開始維修,維修結(jié)束之后把的字段設(shè)置為,以表示廁所可用。 線程間通信 如果一個(gè)線程從頭到尾執(zhí)行完也不和別的線程打交道的話,那就不會(huì)有各種安全性問題了。但是協(xié)作越來越成為社會(huì)發(fā)展的大勢,一個(gè)大任務(wù)拆成若干個(gè)小任務(wù)之后,各個(gè)小任務(wù)之間可能也需要相互協(xié)作最終...
摘要:前文回顧上一篇文章重點(diǎn)嘮叨了中協(xié)調(diào)線程間通信的機(jī)制,它有力的保證了線程間通信的安全性以及便利性。所以同一時(shí)刻廚師線程和服務(wù)員線程不會(huì)同時(shí)在等待隊(duì)列中。對于在操作系統(tǒng)中線程的阻塞狀態(tài),語言中用和這三個(gè)狀態(tài)分別表示。 前文回顧 上一篇文章重點(diǎn)嘮叨了java中協(xié)調(diào)線程間通信的wait/notify機(jī)制,它有力的保證了線程間通信的安全性以及便利性。本篇將介紹wait/notify機(jī)制的一個(gè)應(yīng)用...
摘要:本文對多線程基礎(chǔ)知識進(jìn)行梳理,主要包括多線程的基本使用,對象及變量的并發(fā)訪問,線程間通信,的使用,定時(shí)器,單例模式,以及線程狀態(tài)與線程組。源碼采用構(gòu)建,多線程這部分源碼位于模塊中。通知可能等待該對象的對象鎖的其他線程。 本文對多線程基礎(chǔ)知識進(jìn)行梳理,主要包括多線程的基本使用,對象及變量的并發(fā)訪問,線程間通信,lock的使用,定時(shí)器,單例模式,以及線程狀態(tài)與線程組。 寫在前面 花了一周時(shí)...
摘要:在程序開發(fā)中一定遇到并發(fā)編程的場景雖然我們大部分時(shí)間并不直接使用但是是多線程的基礎(chǔ)面試中也會(huì)總是被問到與線程有關(guān)的問題那么線程都有哪些知識呢最近在研究線程的源碼的時(shí)候也總結(jié)了關(guān)于線程一些基本知識線程是什么線程是輕量級的進(jìn)程是操作系統(tǒng)調(diào)度任務(wù) 在程序開發(fā)中, 一定遇到并發(fā)編程的場景, 雖然我們大部分時(shí)間并不直接使用Thread, 但是Thread是多線程的基礎(chǔ), 面試中也會(huì)總是被問到與線...
閱讀 3221·2021-11-19 09:40
閱讀 3018·2021-09-09 09:32
閱讀 806·2021-09-02 09:55
閱讀 1404·2019-08-26 13:23
閱讀 2426·2019-08-26 11:46
閱讀 1243·2019-08-26 10:19
閱讀 2071·2019-08-23 16:53
閱讀 1086·2019-08-23 12:44