JAVA线程间协作:wait.notify.notifyAll
歡迎跳轉到本文的原文鏈接:https://honeypps.com/java/java-multi-thread-of-wait-notify-notifyAll/
? ??JAVA的進程同步是通過synchronized()來實現的,需要說明的是,JAVA的synchronized()方法類似于操作系統概念中的互斥內存塊,在JAVA中的Object類型中,都是帶有一個內存鎖的,在有線程獲取該內存鎖后,其它線程無法訪問該內存,從而實現JAVA中簡單的同步、互斥操作。明白這個原理,就能理解為什么synchronized(this)與synchronized(static XXX)的區別了,synchronized就是針對內存區塊申請內存鎖,this關鍵字代表類的一個對象,所以其內存鎖是針對相同對象的互斥操作,而static成員屬于類專有,其內存空間為該類所有成員共有,這就導致synchronized()對static成員加鎖,相當于對類加鎖,也就是在該類的所有成員間實現互斥,在同一時間只有一個線程可訪問該類的實例。如果只是簡單的想要實現在JAVA中的線程互斥,明白這些基本就已經夠了。但是如果需要在線程間進行協作通信,就需要借助Object對象的wait,notify和notifyAll方法了。
? ? 在Java中,可以通過配合調用Object對象的wait方法和notify方法或notifyAll方法來實現線程間的協作通信。在線程中調用wait方法,將阻塞等待其他線程的通知(其他線程調用notify方法或notifyAll方法),在線程中調用notify方法或notifyAll方法,將通知其他線程從wait方法處返回。?
? ??Object.wait()與Object.notify()必須要與同步塊或同步方法(synchronized塊或者synchronized方法)一起使用,也就是wait與notify是針對已經獲取了Object鎖進行操作,從語法角度來說就是Object.wait(),Object.notify必須在同步塊或同步方法內。從功能上來說wait就是說線程在獲取對象鎖后,主動釋放對象鎖,同時本線程休眠。直到有其它線程調用對象的notify()喚醒該線程,才能繼續獲取對象鎖,并繼續執行。相應的notify()就是對對象鎖的喚醒操作。但有一點需要注意的是notify()調用后,并不是馬上就釋放對象鎖的,而是在相應的同步塊或同步方法中執行結束,自動釋放鎖后,JVM會在wait()對象鎖的線程中隨機選取一線程,賦予其對象鎖,喚醒線程,繼續執行。這樣就提供了在線程間同步、喚醒的操作。Thread.sleep()與Object.wait()二者都可以暫停當前線程,釋放CPU控制權,主要的區別在于Object.wait()在釋放CPU同時,釋放了對象鎖的控制。
? ??wait方法
? ? 調用線程的sleep,yield方法時,線程并不會讓出對象鎖,wait卻不同。
? ? wait函數必須在同步代碼塊中調用(也就是當前線程必須持有對象的鎖),他的功能是這樣的:
? ? ? ??我累了,休息一會兒,對象的鎖你們拿去用吧,CPU也給你們。
? ? 調用了wait函數的線程會一直等待,直到有其他線程調用了同一個對象的notify或者notifyAll方法才能被喚醒,需要注意的是:被喚醒并不代表立即獲得對象的鎖。也就是說,一個線程調用了對象的wait方法后,他需要等待兩件事情的發生:
????1.?有其他線程調用同一個對象的notify或者notifyAll方法(調用notify/notifyAll方法之前)
????2.?被喚醒之后重新獲得對象的鎖(調用notify/notifyAll方法之后)才能繼續往下執行后續動作。
? ? 如果一個線程調用了某個對象的wait方法,但是后續并沒有其他線程調用該對象的notify或者notifyAll方法,則該線程將會永遠等下去…
? ??notify和notifyAll方法
? ? notify/notifyAll方法也必須在同步代碼塊中調用(也就是調用線程必須持有對象的鎖),他們的功能是這樣的:
? ? ? ??女士們,先生們請注意,鎖的對象我即將用完,請大家醒醒,準備一下,馬上你們就能使用鎖了。
? ? 不同的是,notify方法只會喚醒一個正在等待的線程(至于喚醒誰,不確定!),而notifyAll方法會喚醒所有正在等待的線程。還有一點需要特別強調:調用notify和notifyAll方法后,當前線程并不會立即放棄鎖的持有權,而必須要等待當前同步代碼塊執行完才會讓出鎖。
? ? 如果一個對象之前沒有調用wait方法,那么調用notify方法是沒有任何影響的。
? ? 下面來看一個例子:
package com.cooperation;import java.util.concurrent.TimeUnit;public class Test {public static Object object = new Object();static class Thread1 implements Runnable{@Overridepublic void run(){synchronized(object){System.out.println(Thread.currentThread().getName()+" is running.");try{object.wait();}catch (InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread().getName()+" get the lock.");}}}static class Thread2 implements Runnable{@Overridepublic void run(){synchronized(object){System.out.println(Thread.currentThread().getName()+" is running.");object.notify();System.out.println(Thread.currentThread().getName()+" invoke notify()");System.out.println(Thread.currentThread().getName()+" release the lock.");}}}public static void main(String[] args) throws InterruptedException{Thread thread1 = new Thread(new Thread1());Thread thread2 = new Thread(new Thread2());thread1.start();TimeUnit.SECONDS.sleep(1);thread2.start();} }? ? 運行結果(運行多次,結果相同):
Thread-0 is running. Thread-1 is running. Thread-1 invoke notify() Thread-1 release the lock. Thread-0 get the lock.? ?可以看到當Thread-0調用了wait方法后就釋放了object鎖,Thread-1獲取鎖之后調用notify的釋放鎖,但是這個時候Thread沒有立刻獲取object鎖,而是等到了Thread-1的同步塊退出之后才獲取了object的鎖。
? ? 如果將class Thread2中的?System.out.println(Thread.currentThread().getName()+"?release?the?lock.");這句放在synchronized(object)的外面,有可能出現如下的運行結果:
Thread-0 is running. Thread-1 is running. Thread-1 invoke notify() Thread-0 get the lock. Thread-1 release the lock.? ? 這是因為,當Thread-1釋放了object鎖并且退出了同步塊之后Thread-0立刻獲取了鎖,而這時候兩個線程的打印語句的順序就隨機了。
?
? ? 在JDK1.4之后出現了一個Condition類,這個類也能夠實現相同的功能,并且一般建議使用Condition替代wait,notify,notifyAll家族,實現更安全的線程間協作通信功能,比如ArrayBlockingQueue就是使用Condition實現阻塞隊列的。這個我在以后的博客中會涉及到。
? ? 如果有什么意見或者建議請在下方留言。
歡迎跳轉到本文的原文鏈接:https://honeypps.com/java/java-multi-thread-of-wait-notify-notifyAll/
歡迎支持筆者新作:《深入理解Kafka:核心設計與實踐原理》和《RabbitMQ實戰指南》,同時歡迎關注筆者的微信公眾號:朱小廝的博客。總結
以上是生活随笔為你收集整理的JAVA线程间协作:wait.notify.notifyAll的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JAVA多线程之UncaughtExce
- 下一篇: JAVA线程间协作:Condition