一.概述
Java中所有多線程的實現(xiàn),均通過封裝Thread類實現(xiàn),所以通過源碼深入研究Thread類,對深入理解java多線程很有必要,本文Thread類源碼均基于JDK 1.8。
二.線程的狀態(tài)
通過Thread類的內(nèi)部枚舉類State可以知道, 線程有以下六個狀態(tài)。
public enum State {
/**
* 線程剛創(chuàng)建,尚未啟動(還未調(diào)用 start() 方法)的狀態(tài)
*/
NEW,
/**
* 可運行線程的線程狀態(tài)。調(diào)用了 start() 方法,此時線程已經(jīng)準備好被執(zhí)行,處于就緒隊列中。
* 處于此狀態(tài)的線程是正在JVM中運行的,但可能在等待操作系統(tǒng)中的其他資源,如CPU時間片。
*/
RUNNABLE,
/**
* 阻塞等待監(jiān)視器鎖的狀態(tài)。處于此狀態(tài)的線程正在阻塞等待監(jiān)視器鎖,以進入一個同步塊/方法,
* 或者在執(zhí)行完wait()方法后重入同步塊/方法。
*/
BLOCKED,
/**
* 等待狀態(tài)。執(zhí)行完Object.wait無超時參數(shù)操作,或者 Thread.join無超時參數(shù)操作,
* 或者 LockSupport.park操作后,線程進入等待狀態(tài)。
* 一般在等待狀態(tài)的線程在等待其它線程執(zhí)行特殊操作,例如:
* 等待其它線程調(diào)用Object.notify()喚醒或者Object.notifyAll()喚醒所有。
*/
WAITING,
/**
* 計時等待狀態(tài)。Thread.sleep、Object.wait帶超時時間、Thread.join帶超時時間、
* LockSupport.parkNanos、LockSupport.parkUntil這些操作會使線程進入計時等待狀態(tài)。
*/
TIMED_WAITING,
/**
* 終止狀態(tài),線程執(zhí)行完畢。
*/
TERMINATED;
}
通過下面的線程狀態(tài)轉(zhuǎn)換圖,可以對線程狀態(tài)的轉(zhuǎn)換有更深刻的認識:

創(chuàng)建狀態(tài)(New)
當用new操作符創(chuàng)建一個新的線程對象時,該線程處于創(chuàng)建狀態(tài),尚未啟動(還未調(diào)用 start() 方法)。
處于創(chuàng)建狀態(tài)的線程只是一個空的線程對象,系統(tǒng)不為它分配資源。可運行狀態(tài)(Runnable)
處于創(chuàng)建狀態(tài)的線程調(diào)用start()方法,系統(tǒng)將為線程分配必需的資源(JVM會為其創(chuàng)建程序計數(shù)器和方法調(diào)用棧),則此線程進入可運行狀態(tài)。
線程處于可運行狀態(tài)只說明它具備了運行條件,但可運行狀態(tài)并不一定是正在運行的狀態(tài)。一個線程能否由可運行狀態(tài)變?yōu)檫\行狀態(tài),取決于系統(tǒng)的調(diào)度。運行狀態(tài)(Running)
處于可運行狀態(tài)的線程已經(jīng)具備了運行條件,但還沒有分配到CPU,處于線程就緒隊列(盡管是采用隊列形式,事實上,把它稱為可運行池而不是可運行隊列。因為cpu的調(diào)度不一定是按照先進先出的順序來調(diào)度的),等待系統(tǒng)為其分配CPU。
一旦獲得CPU,線程就進入運行狀態(tài)并自動調(diào)用自己的run方法。不可運行狀態(tài)(Waiting/Blocked/Timed Waiting)
處于運行狀態(tài)的線程最為復雜,它可以變?yōu)榭蛇\行狀態(tài)和不可運行狀態(tài)。例如,對運行狀態(tài)的線程調(diào)用yield()方法,它就會讓出cpu資源,再次變?yōu)榭蛇\行狀態(tài)。
當發(fā)生下列事件時,處于運行狀態(tài)的線程會轉(zhuǎn)入到不可運行狀態(tài):
(1)當線程調(diào)用wait()方法來等待另一個線程的通知,或者等待另一個調(diào)用join()方法的線程執(zhí)行結(jié)束時,線程就會進入等待狀態(tài)。
(2)當一個線程試圖獲取一個內(nèi)部的對象鎖,而該鎖被其他線程持有,則該線程進入阻塞狀態(tài)。
(3)當線程調(diào)用sleep()方法時,傳遞一個超時參數(shù),則會使線程進入計時等待狀態(tài)。
返回可運行狀態(tài):
(1)通知消息到來或另一個調(diào)用join()方法的線程執(zhí)行結(jié)束時,線程會進入可運行狀態(tài)。
(2)當其他線程釋放對象鎖,并且線程調(diào)度器允許本線程持有該鎖時,該線程將變?yōu)榭蛇\行狀態(tài)。
(3) 處于計時等待狀態(tài)的線程在指定的時間過去后,會變?yōu)榭蛇\行狀態(tài)。死亡狀態(tài)
線程正常結(jié)束或因異常退出run()方法,線程進入死亡狀態(tài)。
線程一旦進入死亡狀態(tài),將不再具有運行的資格,所以也不可能再轉(zhuǎn)到其他狀態(tài)。線程一旦死亡,就不能復生。如果在一個死去的線程上調(diào)用start()方法,會拋出java.lang.IllegalThreadStateException異常。
線程會通過以下三種方式進入死亡狀態(tài):
(1)run()方法執(zhí)行完成,線程正常結(jié)束。
(2)線程拋出一個未捕獲的Exception或Error。
(3)直接調(diào)用stop()方法強行終止線程(這個方法不推薦使用,因為stop和suspend、resume一樣,容易導致死鎖)。
三.基本屬性
Thread類中的基本屬性如下所示。
/* Make sure registerNatives is the first thing <clinit> does. */
// 類加載的時候,調(diào)用靜態(tài)的registerNatives()方法, 這個方法是本地方法
private static native void registerNatives();
static {
registerNatives();
}
//線程名字
private volatile String name;
//線程優(yōu)先級
private int priority;
private Thread threadQ;
private long eetop;
//是否是單步執(zhí)行
private boolean single_step;
//是否是守護線程
private boolean daemon = false;
//JVM狀態(tài)
private boolean stillborn = false;
//從構(gòu)造方法傳過來的Runnable,實際要執(zhí)行的線程任務
private Runnable target;
//當前線程所在的線程組
private ThreadGroup group;
//當前線程的上下文類加載器
private ClassLoader contextClassLoader;
//當前線程繼承的訪問控制上下文
private AccessControlContext inheritedAccessControlContext;
//線程的默認編號,用于生成線程的默認名字
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
//當前線程維護的ThreadLocal值,ThreadLocalMap會被ThreadLocal類維護
ThreadLocal.ThreadLocalMap threadLocals = null;
//當前線程維護的從父線程那里繼承的ThreadLocal值
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
//給這個線程設置的棧的大小,默認為0
private long stackSize;
private long nativeParkEventPointer;
// 線程id
private long tid;
//用于生成線程id
private static long threadSeqNumber;
//標識線程狀態(tài),默認是線程未啟動
private volatile int threadStatus = 0;
//得到下個線程id
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
}
volatile Object parkBlocker;
private volatile Interruptible blocker;
private final Object blockerLock = new Object();
//設置blocker字段
void blockedOn(Interruptible b) {
synchronized (blockerLock) {
blocker = b;
}
}
//線程執(zhí)行的最低優(yōu)先級
public final static int MIN_PRIORITY = 1;
//線程執(zhí)行的默認優(yōu)先級
public final static int NORM_PRIORITY = 5;
//線程執(zhí)行的最高的優(yōu)先級
public final static int MAX_PRIORITY = 10;
四.構(gòu)造方法
要創(chuàng)建一個Thread類的實例自然要通過構(gòu)造函數(shù),Thread類的public構(gòu)造函數(shù)有8個之多,但是他們本質(zhì)上都調(diào)用了同一個init函數(shù):
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(String name) {
init(null, null, name, 0);
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
public Thread(ThreadGroup group, Runnable target) {
init(group, target, "Thread-" + nextThreadNum(), 0);
}
public Thread(ThreadGroup group, String name) {
init(group, null, name, 0);
}
public Thread(ThreadGroup group, Runnable target, String name) {
init(group, target, name, 0);
}
public Thread(ThreadGroup group, Runnable target, String name, long stackSize) {
init(group, target, name, stackSize);
}
可見,這八個public類型的構(gòu)造函數(shù)只不過是給init的方法的四個參數(shù)分別賦不同的值, 這四個參數(shù)分別是:
- ThreadGroup g(線程所在線程組)
- Runnable target (Runnable對象)
- String name (線程的名字)
- long stackSize (為線程分配的棧的大小,若為0則表示忽略這個參數(shù))
而init方法又調(diào)用了另一個init方法,設置了AccessControlContext,以及inheritThreadLocals參數(shù):
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
//獲取父線程
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
//判斷線程組參數(shù)是否為空
if (g == null) {
//如果沒有傳入線程組的話, 首先使用SecurityManager中的ThreadGroup
if (security != null) {
g = security.getThreadGroup();
}
//如果從SecurityManager中獲取不到ThreadGroup, 那么就從父線程中獲取線程組
if (g == null) {
g = parent.getThreadGroup();
}
}
g.checkAccess();
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
//初始化線程組
this.group = g;
//子線程繼承父線程的優(yōu)先級和守護屬性
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
//初始化target
this.target = target;
//設置優(yōu)先級
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
//設置棧深度
this.stackSize = stackSize;
//設置線程ID
tid = nextThreadID();
}
五.native方法
//獲得當前正在執(zhí)行的線程的引用
public static native Thread currentThread();
//使當前線程從運行狀態(tài)(Running)變?yōu)榭蛇\行狀態(tài)(Runnable)
public static native void yield();
//強制當前正在執(zhí)行的線程休眠(暫停執(zhí)行),休眠結(jié)束后,線程返回到可運行狀態(tài)
public static native void sleep(long millis) throws InterruptedException;
//啟動線程,為線程分配對應的資源
private native void start0();
//查看當前線程是否被中斷
private native boolean isInterrupted(boolean ClearInterrupted);
//查看當前線程是否存活
public final native boolean isAlive();
//獲取當前線程棧幀的數(shù)量
public native int countStackFrames();
//當且僅當當前線程在指定的對象上持有監(jiān)視器鎖時,才返回 true
public static native boolean holdsLock(Object obj);
private native static StackTraceElement[][] dumpThreads(Thread[] threads);
private native static Thread[] getThreads();
//設置線程優(yōu)先級
private native void setPriority0(int newPriority);
//停止線程
private native void stop0(Object o);
//掛起線程
private native void suspend0();
//將一個掛起線程復活繼續(xù)執(zhí)行
private native void resume0();
//設置該線程的中斷狀態(tài)
private native void interrupt0();
private native void setNativeName(String name);
六.主要方法
1.start()方法
public synchronized void start() {
//線程只能被啟動一次,不能被重復啟動,如果線程已啟動則拋出異常
if (threadStatus != 0)
throw new IllegalThreadStateException();
//向線程組中添加此線程
group.add(this);
boolean started = false;
try {
//調(diào)用native方法
start0();
started = true;
} finally {
try {
if (!started) {
// 如果線程啟動失敗,從線程組里面移除該線程
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
synchronized 關鍵字說明start方法是同步的,并且是啟動這個線程進行執(zhí)行,JVM將會調(diào)用這個線程的run方法。這樣產(chǎn)生的結(jié)果是,兩個線程在并發(fā)執(zhí)行,其中一個是調(diào)用start()方法的線程,另一個是當前thread對象代表的線程,它會執(zhí)行run方法。
2.sleep()方法
public static void sleep(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
//調(diào)用本地方法
sleep(millis);
}
sleep()方法的作是使當前線程休眠一定的時間,讓其他線程有機會繼續(xù)執(zhí)行,但是這個期間是不釋放持有的鎖的,調(diào)用sleep()方法需要捕捉異常。
3.join()方法
join()方法的實現(xiàn)原理,可以看我之前寫的一篇文章:Thread類中join方法的實現(xiàn)原理
4.interrupt()方法
public void interrupt() {
if (this != Thread.currentThread())
//檢查權限
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
//只是設置了中斷標志位
interrupt0();
b.interrupt(this);
return;
}
}
interrupt0();
}
其實調(diào)用interrupt()方法并不是真的中斷線程,只是將Thread中的interrupt標志設置為true,用戶需自行檢測這一變量,停止線程。
其實Thread類中與線程中斷有關的,有三個方法,比較容易混淆,在這里解釋一下。
public void interrupt() //將線程設置為中斷狀態(tài)
public boolean isInterrupted() //判斷是否被中斷
public static boolean interrupted() //判斷是否被中斷,被清除當前中斷狀態(tài)
一般來說,阻塞函數(shù):如Thread.sleep、Thread.join、Object.wait等在檢查到線程的中斷狀態(tài)的時候,會拋出InteruptedExeption, 也就是說,它可以用來中斷一個正處于阻塞狀態(tài)的線程,同時會清除線程的中斷狀態(tài)。
5.exit( )方法
private void exit() {
if (group != null) {
group.threadTerminated(this);
group = null;
}
target = null;
threadLocals = null;
inheritableThreadLocals = null;
inheritedAccessControlContext = null;
blocker = null;
uncaughtExceptionHandler = null;
}
exit( )是由系統(tǒng)調(diào)用的,用于線程在真正的退出前進行一些清理的操作。
參考:
Thread類源碼分析
Java常用類源碼——Thread源碼解析
Thread類源碼解讀(1)——如何創(chuàng)建和啟動線程