再說Java中wait、sleep、join和yield方法的區(qū)別

引入

長期以來,多線程問題頗為受到面試官們的青睞。如果你去參加一個面試,面試官全程沒有問你一個關(guān)于多線程的問題,你都覺得不正規(guī)(哈哈~~)。雖然在我們實際的開發(fā)過程很少會有開發(fā)復(fù)雜多線程應(yīng)用的機會,但是通過深入理解它,會讓你在面試與工作中,變得自信與從容。

關(guān)于java線程基礎(chǔ)

如果對于Java線程基礎(chǔ)不是很了解的同學(xué),可以參考我的另外一篇文章:Java多線程基礎(chǔ)

源碼解讀

wait、sleep、join和yield這四個方法中, sleep,join和yield定義在Thread類中,
wait定義在Object類中。下圖展示了一個線程的生命周期:
Thread類:

package java.lang;
public class Thread implements Runnable {
  /**
   * 向調(diào)度程序發(fā)出的提示,表明當前線程愿意放棄使用當前的處理器資源。調(diào)度程序可以忽略這個提示。
   *  yield是一種啟發(fā)式的改進線程之間的相對進程的嘗試,否則會過度使用CPU。
   *       它的使用應(yīng)該與詳細的分析和基準測試相結(jié)合,以確保它實際具有預(yù)期的效果。
   * 很少適合使用這種方法。對于調(diào)試或測試目的,它可能很有用,因為它可以幫助重現(xiàn)由于競態(tài)條件而產(chǎn)生的錯誤
   */
  public static native void yield();
  /**
   * 使當前正在執(zhí)行的線程在指定的毫秒數(shù)內(nèi)休眠(臨時停止執(zhí)行);
   * 線程在休眠的過程中,不會釋放任何已經(jīng)得到的鎖;
   */
  public static native void sleep(long millis) throws InterruptedException;

  /**
   * 等待線程死亡的時間最多為{millis}毫秒,如果{millis}設(shè)置為0時,將意味著一直等待下去。
   * 此實現(xiàn)使用{this.isAlive()}為條件,循環(huán)調(diào)用{Object.wait()}方法
   */
  public final synchronized void join(long millis) throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
  }
}

我們可以看到j(luò)oin方法最終是以while循環(huán)的形式,通過檢測當前線程isAlive()是否可用,調(diào)用Object.wait()進行阻塞等待。

Object類:

package java.lang;
public class Object {
  public final native void wait(long timeout) throws InterruptedException;
}

所以最終讓線程等待的是Object.wait(), Thread.sleep()和Thread.yield()這三個方法;
而這三個方法都是調(diào)用的C/C++實現(xiàn)的本地方法;

Java線程狀態(tài)

一個線程的生命周期中,總共有以下6種狀態(tài)

  • NEW - 這個狀態(tài)主要是線程未被Thread.start()調(diào)用前的狀態(tài)。
  • RUNNABLE - 線程正在JVM中被執(zhí)行,它可能正在等待來自操作系統(tǒng)(如處理器)的其他資源。
  • BLOCKED - 線程被阻塞等待一個monitor鎖,處于阻塞狀態(tài)的線程正在等待monitor鎖進入synchronized的代碼塊或方法,或者在調(diào)用Object.wait()方法后重新進入synchronized的代碼塊或方法。
  • WAITING - 由于線程調(diào)用了Object.wait(0),Thread.join(0)LockSupport.park其中的一個方法,線程處于等待狀態(tài),其中調(diào)用wait, join方法時未設(shè)置超時時間。還有一種情況,處于等待狀態(tài)的線程正在等待另一個線程執(zhí)行特定的操作,比如:一個線程調(diào)用了Object.wait()后,等待另一個線程調(diào)用Object.notifyAll()Object.notify()方法;或一個線程調(diào)用了Thread.join()方法,等待自己的線程的結(jié)束。
  • TIMED_WAITING - 線程等待一個指定的時間,比如線程調(diào)用了Object.wait(long), Thread.join(long),LockSupport.parkNanos, LockSupport.parkUntil方法之后,線程的狀態(tài)就會變成TIMED_WAITING
  • TERMINATED - 終止的線程狀態(tài),線程已經(jīng)完成執(zhí)行。

下面我繪制出了一張Java線程的生命周期,如下圖:


Java線程生命周期

總結(jié)

  1. sleep、yield方法是靜態(tài)方法;作用的是當前執(zhí)行的線程;
  2. yield方法釋放了cpu的執(zhí)行權(quán),但是依然保留了cpu的執(zhí)行資格。給個簡單的例子:很多人排隊上WC,剛好排上yield上了,現(xiàn)在yield說,出讓它這次機會,與更急的人一起比賽誰能更快進入到WC中去。這個比賽可能是其他的人,也可能就是yield本身;
  3. wait釋放CPU資源,同時釋放鎖;
  4. sleep釋放CPU資源,但不釋放鎖;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容