日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

Java基础——Java多线程中sleep()、wait()和notify()

發(fā)布時間:2023/12/13 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java基础——Java多线程中sleep()、wait()和notify() 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、sleep()


sleep()方法源碼:

/** * Causes the currently executing thread to sleep (temporarily cease * execution) for the specified number of milliseconds, subject to * the precision and accuracy of system timers and schedulers. The thread * does not lose ownership of any monitors. * * @param millis * the length of time to sleep in milliseconds * * @throws IllegalArgumentException * if the value of {@code millis} is negative * * @throws InterruptedException * if any thread has interrupted the current thread. The * <i>interrupted status</i> of the current thread is * cleared when this exception is thrown. */ public static native void sleep(long millis) throws InterruptedException;

sleep()方法來自于Thread類,從源碼給出的解釋來看,sleep()方法可以做到如下幾點:

? ? ? ?(1)sleep()使當前線程進入停滯狀態(tài)(阻塞當前線程),讓出CUP的使用、目的是不讓當前線程獨自霸占該進程所獲的CPU資源,以留一定時間給其他線程執(zhí)行的機會;

? ? ? ?(2)sleep()是Thread類的Static(靜態(tài))的方法;因此他不能改變對象的機鎖,所以當在一個Synchronized塊中調(diào)用Sleep()方法時,線程雖然休眠了,但是對象的機鎖并木有被釋放,其他線程無法訪問這個對象(即使睡著也持有對象鎖)。

? ? ? ?(3)在sleep()休眠時間期滿后,該線程不一定會立即執(zhí)行,這是因為其它線程可能正在運行而且沒有被調(diào)度為放棄執(zhí)行,除非此線程具有更高的優(yōu)先級。


代碼演示:

public class Main { public static void main(String[] args) { Main main = new Main(); main.startThread(); } /** * 啟動線程 */ public void startThread() { Thread t = new Thread(new Runnable() { @Override public void run() { System.out.println("開始執(zhí)行線程。。。"); System.out.println("進入睡眠狀態(tài)。。。"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程結(jié)束。。。"); } }); t.start(); } } 運行結(jié)果: 開始執(zhí)行線程。。。 進入睡眠狀態(tài)。。。 線程結(jié)束。。。從運行的結(jié)果來看,我們可以看出程序雖然在運行過程中中斷了3秒,但是在3秒結(jié)束之后依然會繼續(xù)執(zhí)行代碼,直到運行結(jié)束。在睡眠的期間內(nèi),線程會一直持有monitor對象。


二、wait()


wait()方法源碼:

/** * Causes the current thread to wait until another thread invokes the * {@link java.lang.Object#notify()} method or the * {@link java.lang.Object#notifyAll()} method for this object. * In other words, this method behaves exactly as if it simply * performs the call {@code wait(0)}. * <p> * The current thread must own this object's monitor. The thread * releases ownership of this monitor and waits until another thread * notifies threads waiting on this object's monitor to wake up * either through a call to the {@code notify} method or the * {@code notifyAll} method. The thread then waits until it can * re-obtain ownership of the monitor and resumes execution. * <p> * As in the one argument version, interrupts and spurious wakeups are * possible, and this method should always be used in a loop: * <pre> * synchronized (obj) { * while (<condition does not hold>) * obj.wait(); * ... // Perform action appropriate to condition * } * </pre> * This method should only be called by a thread that is the owner * of this object's monitor. See the {@code notify} method for a * description of the ways in which a thread can become the owner of * a monitor. * * @exception IllegalMonitorStateException if the current thread is not * the owner of the object's monitor. * @exception InterruptedException if any thread interrupted the * current thread before or while the current thread * was waiting for a notification. The <i>interrupted * status</i> of the current thread is cleared when * this exception is thrown. * @see java.lang.Object#notify() * @see java.lang.Object#notifyAll() */ public final void wait() throws InterruptedException { wait(0); }

首先wait()是屬于Object類的方法,從源碼給出的解釋來看,wait()方法可以做到如下幾點:

? ? ? ?(1)wait()方法是Object類里的方法;當一個線程執(zhí)行到wait()方法時,它就進入到一個和該對象相關(guān)的等待池中,同時失去(釋放)了對象的機鎖(暫時失去機鎖,wait(long timeout)超時時間到后還需要返還對象鎖);其他線程可以訪問;

? ? ? ?(2)每個線程必須持有該對象的monitor。如果在當前線程中調(diào)用wait()方法之后,該線程就會釋放monitor的持有對象并讓自己處于等待狀態(tài)。

? ? ? ?(3)如果想喚醒一個正在等待的線程,那么需要開啟一個線程通過notify()或者notifyAll()方法去通知正在等待的線程獲取monitor對象。如此,該線程即可打破等待的狀態(tài)繼續(xù)執(zhí)行代碼。

? ? ? ?(4)wiat()必須放在synchronized block中,否則會在program runtime時扔出”java.lang.IllegalMonitorStateException“異常。


代碼演示:

public class Main { public static void main(String[] args) { Main main = new Main(); main.startThread(); } /** * 線程鎖 */ private final Object object = new Object(); /** * 啟動線程 */ public void startThread() { Thread t = new Thread(new Runnable() { @Override public void run() { System.out.println("開始執(zhí)行線程。。。"); System.out.println("進入等待狀態(tài)。。。"); synchronized (object) { try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("線程結(jié)束。。。"); } }); t.start(); } }

? ? ? ?從代碼來看,在執(zhí)行線程和線程結(jié)束之間,我們先讓該線程獲取object對象作為自己的object's monitor,然后調(diào)用了object對象的wait()方法從而讓其進入等待狀態(tài)。那么程序運行的結(jié)果如下:

開始執(zhí)行線程。。。 進入等待狀態(tài)。。。程序在未被喚醒之后,將不再打印“線程結(jié)束”,并且程序無法執(zhí)行完畢一直處于等待狀態(tài)。


那么從以上的理論和實踐來分析,我們能得出如下結(jié)論:

? ? ? ?(1)在線程的運行過程中,調(diào)用該線程持有monitor對象的wait()方法時,該線程首先會進入等待狀態(tài),并將自己持有的monitor對象釋放。

? ? ? ?(2)如果一個線程正處于等待狀態(tài)時,那么喚醒它的辦法就是開啟一個新的線程,通過notify()或者notifyAll()的方式去喚醒。當然,需要注意的一點就是,必須是同一個monitor對象。

? ? ? ?(3)sleep()方法雖然會使線程中斷,但是不會將自己的monitor對象釋放,在中斷結(jié)束后,依然能夠保持代碼繼續(xù)執(zhí)行。


三、notify()和notifyAll()


說完了sleep()和wait()方法之后,我們接下來討論一下Object類中的另外兩個與wait()相關(guān)的方法。首先還是通過源碼的方式讓大家先初步了解一下:

/** * 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. The choice is arbitrary and occurs at * the discretion of the implementation. A thread waits on an object's * monitor by calling one of the {@code wait} methods. * <p> * The awakened thread will not be able to proceed until the current * thread relinquishes the lock on this object. The awakened thread will * compete in the usual manner with any other threads that might be * actively competing to synchronize on this object; for example, the * awakened thread enjoys no reliable privilege or disadvantage in being * the next thread to lock this object. * <p> * This method should only be called by a thread that is the owner * of this object's monitor. A thread becomes the owner of the * object's monitor in one of three ways: * <ul> * <li>By executing a synchronized instance method of that object. * <li>By executing the body of a {@code synchronized} statement * that synchronizes on the object. * <li>For objects of type {@code Class,} by executing a * synchronized static method of that class. * </ul> * <p> * Only one thread at a time can own an object's monitor. * * @exception IllegalMonitorStateException if the current thread is not * the owner of this object's monitor. * @see java.lang.Object#notifyAll() * @see java.lang.Object#wait() */ public final native void notify();

先來看下notify()這個方法,通過閱讀源碼我們可以總結(jié)一下幾點: (1)當一個線程處于wait()狀態(tài)時,也即等待它之前所持有的object's monitor被釋放,通過notify()方法可以讓該線程重新處于活動狀態(tài),從而去搶奪object's monitor,喚醒該線程。 ? ? ? ?(2)如果多個線程同時處于等待狀態(tài),那么調(diào)用notify()方法只能隨機喚醒一個線程。 ? ? ? ?(3)在同一時間內(nèi),只有一個線程能夠獲得object's monitor,執(zhí)行完畢之后,則再將其釋放供其它線程搶占。 當然,如何使線程成為object‘s monitor的持有者,我會在多線程的其他博客中講解。
接下來,我們再來看看notifyAll()方法: /** * Wakes up all threads that are waiting on this object's monitor. A * thread waits on an object's monitor by calling one of the * {@code wait} methods. * <p> * The awakened threads will not be able to proceed until the current * thread relinquishes the lock on this object. The awakened threads * will compete in the usual manner with any other threads that might * be actively competing to synchronize on this object; for example, * the awakened threads enjoy no reliable privilege or disadvantage in * being the next thread to lock this object. * <p> * This method should only be called by a thread that is the owner * of this object's monitor. See the {@code notify} method for a * description of the ways in which a thread can become the owner of * a monitor. * * @exception IllegalMonitorStateException if the current thread is not * the owner of this object's monitor. * @see java.lang.Object#notify() * @see java.lang.Object#wait() */ public final native void notifyAll(); 那么顧名思義,notifyAll()就是用來喚醒正在等待狀態(tài)中的所有線程的,不過也需要注意以下幾點:(1)notifyAll()只會喚醒那些等待搶占指定object's monitor的線程,其他線程則不會被喚醒。? ? ? ?(2)notifyAll()只會一個一個的喚醒,而并非統(tǒng)一喚醒。因為在同一時間內(nèi),只有一個線程能夠持有object's monitor? ? ? ?(3)notifyAll()只是隨機的喚醒線程,并非有序喚醒。那么如何做到有序喚醒是我們接下來要討論的問題。

notify()實現(xiàn)有序喚醒的思路和實現(xiàn)

就上節(jié)提出的問題,我們在這節(jié)中可以進行一下思考和討論。首先,簡單來說,我們需要去解決的其實就是對于多線程針對object's monitor的有序化。那么根據(jù)這一思路,我直接上代碼:public class MyThreadFactory { // 線程A是否處于等待狀態(tài)的標志 private boolean isThreadAWaiting; // 線程B是否處于等待狀態(tài)的標志 private boolean isThreadBWaiting; // 線程C是否處于等待狀態(tài)的標志 private boolean isThreadCWaiting; public MyThreadFactory() { isThreadAWaiting = true; isThreadBWaiting = true; isThreadCWaiting = true; } /** * 對象鎖 */ private final Object object = new Object(); /** * 該線程作為一個喚醒線程 */ public void startWakenThread() { Thread t = new Thread(new Runnable() { @Override public void run() { synchronized (object) { System.out.println("喚醒線程開始執(zhí)行..."); // 首先釋放線程A quitThreadA(); } } }); t.start(); } /** * 啟動線程A */ public void startThreadA() { Thread t = new Thread(new Runnable() { @Override public void run() { synchronized (object) { System.out.println("線程A開始等待..."); try { for (; ; ) { if (!isThreadAWaiting) break; object.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程A結(jié)束..."); // 線程A結(jié)束后,暫停2秒釋放線程B try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } quitThreadB(); } } }); t.start(); } /** * 啟動線程B */ public void startThreadB() { Thread t = new Thread(new Runnable() { @Override public void run() { synchronized (object) { System.out.println("線程B開始等待..."); try { for (; ; ) { if (!isThreadBWaiting) break; object.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程B結(jié)束..."); // 線程B結(jié)束后,暫停2秒釋放線程C try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } quitThreadC(); } } }); t.start(); } /** * 啟動線程C */ public void startThreadC() { Thread t = new Thread(new Runnable() { @Override public void run() { synchronized (object) { System.out.println("線程C開始等待..."); try { for (; ; ) { if (!isThreadCWaiting) break; object.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程C結(jié)束..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("所有線程執(zhí)行完畢!"); } } }); t.start(); } /** * 線程A退出等待 */ private void quitThreadA() { isThreadAWaiting = false; object.notify(); } /** * 線程B退出等待 */ private void quitThreadB() { isThreadBWaiting = false; object.notify(); } /** * 線程C退出等待 */ private void quitThreadC() { isThreadCWaiting = false; object.notify(); } } 在以上代碼中,我寫了三個線程A,B,C用來作為等待線程,并且最后通過一個喚醒線程來喚醒這三個線程。我的思路是這樣的:(1)通過notify()方法可以保證每次只喚醒一個線程,但是不能確保喚醒的是哪個線程。(2)在線程A,B,C中,添加for或者while循環(huán)的方式使其進入無限等待的狀態(tài)。這樣能夠保證notify()無論如何都不能喚醒線程。(3)分別給A,B,C線程設(shè)置各自的標記,如果要喚醒該線程的話,就改變其狀態(tài)并且跳出死循環(huán),在最后執(zhí)行下一個線程。
那么最終調(diào)用的main函數(shù)如下: public static void main(String[] args) { MyThreadFactory factory = new MyThreadFactory(); factory.startThreadA(); factory.startThreadB(); factory.startThreadC(); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } factory.startWakenThread(); }運行結(jié)果:線程A開始等待... 線程B開始等待... 線程C開始等待... 喚醒線程開始執(zhí)行... 線程A結(jié)束... 線程B結(jié)束... 線程C結(jié)束... 所有線程執(zhí)行完畢...

總結(jié)

以上是生活随笔為你收集整理的Java基础——Java多线程中sleep()、wait()和notify()的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。