您好,登錄后才能下訂單哦!
這篇文章主要講解了“java線程等待喚醒機制”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“java線程等待喚醒機制”吧!
1)3種讓線程等待和喚醒的方法
方式1:使用Object中的wait()方法讓線程等待,使用Object中的notify()方法喚醒線程
1、wait()、notify/notifyAll() 方法是Object的本地final方法,無法被重寫。
2、wait()使當前線程阻塞,前提是 必須先獲得鎖,一般配合synchronized 關鍵字使用,即,一般在synchronized 同步代碼塊里使用 wait()、notify/notifyAll() 方法。
3、 由于 wait()、notify/notifyAll() 在synchronized 代碼塊執行,說明當前線程一定是獲取了鎖的。
當線程執行wait()方法時候,會釋放當前的鎖,然后讓出CPU,進入等待狀態。
只有當 notify/notifyAll() 被執行時候,才會喚醒一個或多個正處于等待狀態的線程,然后繼續往下執行,直到執行完synchronized 代碼塊的代碼或是中途遇到wait() ,再次釋放鎖。
也就是說,notify/notifyAll() 的執行只是喚醒沉睡的線程,而不會立即釋放鎖,鎖的釋放要看代碼塊的具體執行情況。所以在編程中,盡量在使用了notify/notifyAll() 后立即退出臨界區,以喚醒其他線程讓其獲得鎖
4、wait() 需要被try catch包圍,以便發生異常中斷也可以使wait等待的線程喚醒。
5、notify 和wait 的順序不能錯,如果A線程先執行notify方法,B線程在執行wait方法,那么B線程是無法被喚醒的。
6、notify 和 notifyAll的區別
notify方法只喚醒一個等待(對象的)線程并使該線程開始執行。所以如果有多個線程等待一個對象,這個方法只會喚醒其中一個線程,選擇哪個線程取決于操作系統對多線程管理的實現。notifyAll 會喚醒所有等待(對象的)線程,盡管哪一個線程將會第一個處理取決于操作系統的實現。如果當前情況下有多個線程需要被喚醒,推薦使用notifyAll 方法。比如在生產者-消費者里面的使用,每次都需要喚醒所有的消費者或是生產者,以判斷程序是否可以繼續往下執行。
7、在多線程中要測試某個條件的變化,使用if 還是while?
要注意,notify喚醒沉睡的線程后,線程會接著上次的執行繼續往下執行。所以在進行條件判斷時候,可以先把 wait 語句忽略不計來進行考慮;顯然,要確保程序一定要執行,并且要保證程序直到滿足一定的條件再執行,要使用while進行等待,直到滿足條件才繼續往下執行
package com.lyy.juc;import java.util.concurrent.TimeUnit;public class LockSupportDemo {public static void main(String[] args)//main方法,主線程一切程序入口 {Object objectLock = new Object(); //同一把鎖,類似資源類 new Thread(() -> {synchronized (objectLock) {try {objectLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }System.out.println(Thread.currentThread().getName() + "\t" + "被喚醒了"); }, "t1").start();//暫停幾秒鐘線程 try {TimeUnit.SECONDS.sleep(3L); } catch (InterruptedException e) { e.printStackTrace(); }new Thread(() -> {synchronized (objectLock) {objectLock.notify(); }//objectLock.notify(); /*synchronized (objectLock) { try { objectLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }*/ }, "t2").start(); } }
三秒后打印
t1 被喚醒了
方式2:使用JUC包中Condition的await()方法讓線程等待,使用signal()方法喚醒線程
package com.lyy.juc;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class LockSupportDemo2 {public static void main(String[] args) {Lock lock = new ReentrantLock();Condition condition = lock.newCondition();new Thread(() -> {lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t" + "start");condition.await();System.out.println(Thread.currentThread().getName() + "\t" + "被喚醒"); } catch (InterruptedException e) { e.printStackTrace(); } finally {lock.unlock(); } }, "t1").start();//暫停幾秒鐘線程 try {TimeUnit.SECONDS.sleep(3L); } catch (InterruptedException e) { e.printStackTrace(); }new Thread(() -> {lock.lock();try {condition.signal(); } catch (Exception e) { e.printStackTrace(); } finally {lock.unlock(); }System.out.println(Thread.currentThread().getName() + "\t" + "通知了"); }, "t2").start(); } }
t1 線程在前,先執行start 然后,main執行睡三秒,t2執行start ,先執行await方法,后執行signal ,所以先打印t1 被喚醒,t2 被通知在后
但我把順序換一下,打印結果順序就不一樣了
package com.lyy.juc;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class LockSupportDemo2 {public static void main(String[] args) {Lock lock = new ReentrantLock();Condition condition = lock.newCondition();//暫停幾秒鐘線程 try {TimeUnit.SECONDS.sleep(3L); } catch (InterruptedException e) { e.printStackTrace(); }new Thread(() -> {lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t" + "start");condition.await();System.out.println(Thread.currentThread().getName() + "\t" + "被喚醒"); } catch (InterruptedException e) { e.printStackTrace(); } finally {lock.unlock(); } }, "t1").start();new Thread(() -> {lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t" + "start");condition.signal(); } catch (Exception e) { e.printStackTrace(); } finally {lock.unlock(); }System.out.println(Thread.currentThread().getName() + "\t" + "通知了"); }, "t2").start(); } }
Object和Condition使用的限制條件
線程先要獲得并持有鎖,必須在鎖塊(synchronized或lock)中
方式3:LockSupport類可以阻塞park當前線程以及喚醒unpark指定被阻塞的線程
通過park()和unpark(thread)方法來實現阻塞和喚醒線程的操作
LockSupport是用來創建鎖和其他同步類的基本線程阻塞原語。
LockSupport類使用了一種名為Permit(許可)的概念來做到阻塞和喚醒線程的功能, 每個線程都有一個許可(permit),
permit只有兩個值1和零,默認是零。
可以把許可看成是一種(0,1)信號量(Semaphore),但與 Semaphore 不同的是,許可的累加上限是1。
park() /park(Object blocker)
不傳阻塞當前線程/傳阻塞傳入的具體線程
unpark(Thread thread) 必須指定要喚醒的線程
package com.lyy.juc;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.LockSupport;public class LockSupportDemo3 {public static void main(String[] args) {//正常使用+不需要鎖塊 Thread t1 = new Thread(() -> {System.out.println(Thread.currentThread().getName() + " " + "1111111111111");LockSupport.park();System.out.println(Thread.currentThread().getName() + " " + "2222222222222------end被喚醒"); }, "t1");t1.start();//暫停幾秒鐘線程 try {TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }LockSupport.unpark(t1);System.out.println(Thread.currentThread().getName() + " -----LockSupport.unparrk() invoked over"); } }
permit默認是零,所以一開始調用park()方法,當前線程就會阻塞,直到別的線程將當前線程的permit設置為1時,park方法會被喚醒,
然后會將permit再次設置為零并返回。
調用unpark(thread)方法后,就會將thread線程的許可permit設置成1(注意多次調用unpark方法,不會累加,permit值還是1)會自動喚醒thread線程,即之前阻塞中的LockSupport.park()方法會立即返回。
感謝各位的閱讀,以上就是“java線程等待喚醒機制”的內容了,經過本文的學習后,相信大家對java線程等待喚醒機制這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。