Java基礎——Java多線程中sleep()、wait()和notify()__Java

來源:互聯網
上載者:User

一、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(); 

先來看下notify()這個方法,通過閱讀源碼我們可以總結一下幾點:         (1)當一個線程處於wait()狀態時,也即等待它之前所持有的object's monitor被釋放,通過notify()方法可以讓該線程重新處於活動狀態,從而去搶奪object's monitor,喚醒該線程。        (2)如果多個線程同時處於等待狀態,那麼調用notify()方法只能隨機喚醒一個線程。        (3)在同一時間內,只有一個線程能夠獲得object's monitor,執行完畢之後,則再將其釋放供其它線程搶佔。 當然,如何使線程成為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()就是用來喚醒正在等待狀態中的所有線程的,不過也需要注意以下幾點:         (1)notifyAll()只會喚醒那些等待搶佔指定object's monitor的線程,其他線程則不會被喚醒。        (2)notifyAll()只會一個一個的喚醒,而並非統一喚醒。因為在同一時間內,只有一個線程能夠持有object's monitor        (3)notifyAll()只是隨機的喚醒線程,並非有序喚醒。 那麼如何做到有序喚醒是我們接下來要討論的問題。

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

就上節提出的問題,我們在這節中可以進行一下思考和討論。 首先,簡單來說,我們需要去解決的其實就是對於多線程針對object's monitor的有序化。那麼根據這一思路,我直接上代碼:

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();      }  } 
在以上代碼中,我寫了三個線程A,B,C用來作為等待線程,並且最後通過一個喚醒線程來喚醒這三個線程。 我的思路是這樣的: (1)通過notify()方法可以保證每次只喚醒一個線程,但是不能確保喚醒的是哪個線程。 (2)線上程A,B,C中,添加for或者while迴圈的方式使其進入無限等待的狀態。這樣能夠保證notify()無論如何都不能喚醒線程。 (3)分別給A,B,C線程設定各自的標記,如果要喚醒該線程的話,就改變其狀態並且跳出死迴圈,在最後執行下一個線程。
那麼最終調用的main函數如下:
    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結束...所有線程執行完畢...

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.