一、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()使當前線程進入停滯狀態(阻塞當前線程),讓出CUP的使用、目的是不讓當前線程獨自霸占該進程所獲的CPU資源,以留一定時間給其他線程執行的機會;
? ? ? ?(2)sleep()是Thread類的Static(靜態)的方法;因此他不能改變對象的機鎖,所以當在一個Synchronized塊中調用Sleep()方法時,線程雖然休眠了,但是對象的機鎖并木有被釋放,其他線程無法訪問這個對象(即使睡著也持有對象鎖)。
? ? ? ?(3)在sleep()休眠時間期滿后,該線程不一定會立即執行,這是因為其它線程可能正在運行而且沒有被調度為放棄執行,除非此線程具有更高的優先級。
代碼演示:
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("開始執行線程。。。"); System.out.println("進入睡眠狀態。。。"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程結束。。。"); } }); t.start(); }
}
運行結果:
開始執行線程。。。
進入睡眠狀態。。。
線程結束。。。
從運行的結果來看,我們可以看出程序雖然在運行過程中中斷了3秒,但是在3秒結束之后依然會繼續執行代碼,直到運行結束。在睡眠的期間內,線程會一直持有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類里的方法;當一個線程執行到wait()方法時,它就進入到一個和該對象相關的等待池中,同時失去(釋放)了對象的機鎖(暫時失去機鎖,wait(long timeout)超時時間到后還需要返還對象鎖);其他線程可以訪問;
? ? ? ?(2)每個線程必須持有該對象的monitor。如果在當前線程中調用wait()方法之后,該線程就會釋放monitor的持有對象并讓自己處于等待狀態。
? ? ? ?(3)如果想喚醒一個正在等待的線程,那么需要開啟一個線程通過notify()或者notifyAll()方法去通知正在等待的線程獲取monitor對象。如此,該線程即可打破等待的狀態繼續執行代碼。
? ? ? ?(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("開始執行線程。。。"); System.out.println("進入等待狀態。。。"); synchronized (object) { try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("線程結束。。。"); } }); t.start(); }
}
? ? ? ?從代碼來看,在執行線程和線程結束之間,我們先讓該線程獲取object對象作為自己的object's monitor,然后調用了object對象的wait()方法從而讓其進入等待狀態。那么程序運行的結果如下:
開始執行線程。。。
進入等待狀態。。。
程序在未被喚醒之后,將不再打印“線程結束”,并且程序無法執行完畢一直處于等待狀態。
那么從以上的理論和實踐來分析,我們能得出如下結論:
? ? ? ?(1)在線程的運行過程中,調用該線程持有monitor對象的wait()方法時,該線程首先會進入等待狀態,并將自己持有的monitor對象釋放。
? ? ? ?(2)如果一個線程正處于等待狀態時,那么喚醒它的辦法就是開啟一個新的線程,通過notify()或者notifyAll()的方式去喚醒。當然,需要注意的一點就是,必須是同一個monitor對象。
? ? ? ?(3)sleep()方法雖然會使線程中斷,但是不會將自己的monitor對象釋放,在中斷結束后,依然能夠保持代碼繼續執行。
三、notify()和notifyAll()
說完了sleep()和wait()方法之后,我們接下來討論一下Object類中的另外兩個與wait()相關的方法。首先還是通過源碼的方式讓大家先初步了解一下:
/** * 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();
/** * 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();
notify()實現有序喚醒的思路和實現
public class MyThreadFactory { // 線程A是否處于等待狀態的標志 private boolean isThreadAWaiting; // 線程B是否處于等待狀態的標志 private boolean isThreadBWaiting; // 線程C是否處于等待狀態的標志 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("喚醒線程開始執行..."); // 首先釋放線程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結束..."); // 線程A結束后,暫停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結束..."); // 線程B結束后,暫停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結束..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("所有線程執行完畢!"); } } }); 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(); }
}
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(); }
運行結果:線程A開始等待...
線程B開始等待...
線程C開始等待...
喚醒線程開始執行...
線程A結束...
線程B結束...
線程C結束...
所有線程執行完畢...