Java Object的wait和notify方法使用詳解
Java Object的wait和notify方法
1 結(jié)論先交代出來
wait()、notify()/notifyAll()方法是Object的本地 final方法,無法被重寫。wait()使當(dāng)前線程阻塞,前提是必須先獲得鎖,一般配合synchronized關(guān)鍵字使用,一般在synchronized 同步代碼塊里使用wait()、notify()/notifyAll()方法。wait()需要被try catch包圍,中斷也可以使wait等待的線程喚醒。notify()和wait()的順序不能錯,如果A線程先執(zhí)行notify()方法,B線程在執(zhí)行wait()方法,那么B線程是無法被喚醒的。- 由于
wait()、notify()/notifyAll()在synchronized代碼塊執(zhí)行,說明當(dāng)前線程一定是獲取了鎖的。
當(dāng)線程執(zhí)行 wait() 方法時候,會釋放當(dāng)前的鎖,然后讓出CPU,進入等待狀態(tài)。只有當(dāng) notify() / notifyAll() 被執(zhí)行時候,才會喚醒一個或多個正處于等待狀態(tài)的線程,然后繼續(xù)往下執(zhí)行,直到執(zhí)行完 synchronized 代碼塊的代碼或是中途遇到 wait() ,再次釋放鎖。
也就是說,notify / notifyAll() 的執(zhí)行只是喚醒沉睡的線程,而不會立即釋放鎖,鎖的釋放要看代碼塊的具體執(zhí)行情況。所以在編程中,盡量在使用了 notify / notifyAll() 后立即退出臨界區(qū),以喚醒其他線程
2 notify 和 notifyAll 的區(qū)別
notify方法只喚醒一個等待(對象的)線程并使該線程開始執(zhí)行。所以如果有多個線程等待一個對象,這個方法只會喚醒其中一個線程,選擇哪個線程取決于操作系統(tǒng)對多線程管理的實現(xiàn)。
notifyAll 會喚醒所有等待(對象的)線程,盡管哪一個線程將會第一個處理取決于操作系統(tǒng)的實現(xiàn)。如果當(dāng)前情況下有多個線程需要被喚醒,推薦使用notifyAll 方法。
比如在生產(chǎn)者-消費者里面的使用,每次都需要喚醒所有的消費者或是生產(chǎn)者,以判斷程序是否可以繼續(xù)往下執(zhí)行。
3 wait沒有獲取鎖的情況
public class BB {
private void bb() {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("bbbb");
}
public static void main(String[] args) {
new BB().bb();
}
}異常:非法監(jiān)視器狀態(tài)異常
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.shigeqiu.demo.BB.bb(BB.java:10)
at com.shigeqiu.demo.BB.main(BB.java:19)
4 notify沒有獲取鎖的情況
public class BB {
private void bb() {
this.notify();
System.out.println("bbbb");
}
public static void main(String[] args) {
new BB().bb();
}
}異常:非法監(jiān)視器狀態(tài)異常
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at com.shigeqiu.demo.BB.bb(BB.java:9)
at com.shigeqiu.demo.BB.main(BB.java:14)
5 wait和notify執(zhí)行順序情況
public class WaitNotifyOrder {
public static void main(String[] args) throws InterruptedException {
MyLock myLock = new MyLock();
Thread threadA = new Thread(new ThreadA(myLock));
Thread threadB = new Thread(new ThreadB(myLock));
threadA.start();
Thread.sleep(1000);
threadB.start();
}
static class MyLock {
}
static class ThreadA implements Runnable {
private MyLock myLock;
public ThreadA(MyLock myLock) {
this.myLock = myLock;
}
@Override
public void run() {
synchronized (myLock) {
System.out.println("A before");
try {
myLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A after");
}
}
}
static class ThreadB implements Runnable {
private MyLock myLock;
public ThreadB(MyLock myLock) {
this.myLock = myLock;
}
@Override
public void run() {
synchronized (myLock) {
System.out.println('B');
myLock.notify();
}
}
}
}輸出如下:
A before
B
A after
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java高性能新一代構(gòu)建工具Maven-mvnd(實踐可行版)
這篇文章主要介紹了Java高性能新一代構(gòu)建工具Maven-mvnd(實踐可行版),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-06-06
SpringMVC 跨重定向請求傳遞數(shù)據(jù)的方法實現(xiàn)
這篇文章主要介紹了SpringMVC 跨重定向請求傳遞數(shù)據(jù)的方法實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
MapStruct對象映射轉(zhuǎn)換解決Bean屬性拷貝性能問題
無意間看到項目中有小伙伴用到了 MapStruct 來做對象映射轉(zhuǎn)換當(dāng)時我就很好奇,這個是什么框架,能夠解決什么問題,帶著這兩個疑問就有了下面的文章2022-02-02

