引入
長期以來,多線程問題頗為受到面試官們的青睞。如果你去參加一個面試,面試官全程沒有問你一個關(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é)
- sleep、yield方法是靜態(tài)方法;作用的是
當前執(zhí)行的線程; - yield方法釋放了cpu的執(zhí)行權(quán),但是依然保留了cpu的執(zhí)行資格。給個簡單的例子:很多人排隊上WC,剛好排上yield上了,現(xiàn)在yield說,出讓它這次機會,與更急的人一起比賽誰能更快進入到WC中去。這個比賽可能是其他的人,也可能就是yield本身;
- wait釋放CPU資源,同時釋放鎖;
- sleep釋放CPU資源,但不釋放鎖;