Thread多線程源碼剖析
源碼剖析
package java.lang;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.LockSupport;
import sun.nio.ch.Interruptible;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
import sun.security.util.SecurityConstants;
/**
* 線程就是程序中一個線程的執行.JVM允許一個應用中多個線程併發執行.
*
* 每個線程都有優先級.高優先級線程優先於低優先級線程執行.
* 每個線程都可以(不可以)被標記爲守護線程.
* 當線程中的run()方法代碼裏面又創建了一個新的線程對象時,新創建的線程優先級和父線程優先級一樣.
* 當且僅當父線程爲守護線程時,新創建的線程纔會是守護線程.
*
* 當JVM啓動時,通常會有唯一的一個非守護線程(這一線程用於調用指定類的main()方法)
* JVM會持續執行線程直到下面情況某一個發生爲止:
* 1.類運行時exit()方法被調用 且 安全機制允許此exit()方法的調用.
* 2.所有非守護類型的線程均已經終止,or run()方法調用返回 or 在run()方法外部拋出了一些可傳播性的異常.
*
*
* 有2種方式可以創建一個可執行線程.
* 1.定義一個繼承Thread類的子類.子類可覆寫父類的run()方法.子類實例分配內存後可運行(非立即,取決於CPU調用)
* 比如:計算大於指定值的素數的線程可以寫成如下
* class PrimeThread extends Thread {
* long minPrime;
* PrimeThread(long minPrime) {
* this.minPrime = minPrime;
* }
*
* public void run() {
* // compute primes larger than minPrime
* . . .
* }
* }
* 下面的代碼將創建一個線程並啓動它.
* PrimeThread p = new PrimeThread(143);
* p.start();
*
* 2.另一個實現線程的方式就是使類實現Runnable接口.
* 此類自己會實現run()方法.然後此線程會被分配內存,當線程被創建時,會傳入一個參數,然後開始執行.
* 此種方式的樣例代碼如下:
*
* class PrimeRun implements Runnable {
* long minPrime;
* PrimeRun(long minPrime) {
* this.minPrime = minPrime;
* }
*
* public void run() {
* // compute primes larger than minPrime
* . . .
* }
* }
* 下面的代碼能夠創建一個線程並開始執行:
* PrimeRun p = new PrimeRun(143);
* new Thread(p).start();
*
* 每一個線程都有一個用於目的標識的名字.多個線程可以有相同的名字.
* 線程被創建時如果名字沒有被指定,則系統爲其自動生成一個新的名字.
*
* 除非特別說明,否則在創建線程時傳入一個null參數到構造器或者方法會拋出空指針異常NullPointerException
*
* @author unascribed
* @see Runnable
* @see Runtime#exit(int)
* @see #run()
* @see #stop()
* @since JDK1.0
*/
public class Thread implements Runnable {
//確保本地註冊(類構造器方法方法用於類初始化)是創建一個線程首要做的事情.
private static native void registerNatives();//註冊的都是一些本地方法
static {
registerNatives();
}
private volatile String name; //線程名:可更改且線程可見
private int priority; //線程優先級,用int表示,有10級,初始爲5
private Thread threadQ; //未知
private long eetop; //JVM中的JavaThread指針
//是否單步執行此線程
private boolean single_step;
//此線程是否爲守護線程
private boolean daemon = false;
//虛擬機(JVM)狀態
private boolean stillborn = false;
//用來引用構造函數中傳遞的Runnable參數,run()方法執行的目標代碼
private Runnable target;
//線程所屬的組
private ThreadGroup group;
//線程用的上下文類加載器,該上下文類加載器可供線程加載類和資源
private ClassLoader contextClassLoader;
//此線程繼承的訪問(請求)控制上下文
private AccessControlContext inheritedAccessControlContext;
//計數變量,用在nextThreadNum方法中爲匿名線程生成名稱
private static int threadInitNumber;
//靜態加鎖的自增threadInitNumber變量方法,每調用一次,變量加一
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
//此線程的本地變量值.此map由ThreadLocal類進行維護,因爲這個類在ThreadLocal中是包級私有的.
//ThreadLocalMap是一個用於維護線程本地變量的hashmap,此hashmap的key引用類型爲弱引用,這是爲了支持大且長期存活的使用方法.
ThreadLocal.ThreadLocalMap threadLocals = null;
//和此線程相關的由繼承得到的本地變量值.此hashmap由InheritableThreadLocal類進行維護.
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
//此線程請求棧的深度,如果線程創建者未指定棧深度則其值爲0.
//此數字如何被使用完全取決於虛擬機自己;也有一些虛擬機會忽略此變量值.
private long stackSize;
//此變量表示:在本地線程終止後,JVM私有的一個狀態值
private long nativeParkEventPointer;
//線程的ID
private long tid;
//用於生成線程ID
private static long threadSeqNumber;
//爲工具提供的線程狀態值,初始化值表示當前線程還未運行
private volatile int threadStatus = 0;
//私有同步方法,獲取下一個線程id
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
}
/**
* 此變量爲用於調用java.util.concurrent.locks.LockSupport.park方法的參數.
* 其值由方法(private) java.util.concurrent.locks.LockSupport.setBlocker進行設定.
* 其值訪問由方法java.util.concurrent.locks.LockSupport.getBlocker進行獲取.
*/
volatile Object parkBlocker;
/*
* 在可中斷I/O操作中,本線程中的此對象會被阻塞.
* 如果此線程的中斷狀態位被設置,則應該調用此阻塞對象的中斷(interrupt)方法.
*/
private volatile Interruptible blocker;
//設置上述blocker變量時用的鎖
private final Object blockerLock = new Object();
//設定block變量的值;通過java.nio代碼中的 sun.misc.SharedSecrets進行調用.
void blockedOn(Interruptible b) {
synchronized (blockerLock) {
blocker = b;
}
}
//一個線程可以擁有的最低優先級
public final static int MIN_PRIORITY = 1;
//線程的默認優先級
public final static int NORM_PRIORITY = 5;
//一個線程可以擁有的最高優先級.
public final static int MAX_PRIORITY = 10;
//返回當前正在執行線程對象的引用,需注意這是一個本地方法
public static native Thread currentThread();
/**
* 提示線程調度器當前線程願意放棄當前CPU的使用。當然調度器可以忽略這個提示。
*
* 讓出CPU是一種啓發式的嘗試,以改善線程之間的相對進展,否則將過度利用CPU。
* 它的使用應該與詳細的分析和基準測試相結合以確保它實際上具有預期的效果。
*
* 此方法很少有適用的場景.它可以用於debug或者test,通過跟蹤條件可以重現bug.
* 當設計併發控制結構(如java.util.concurrent.locks包中的併發結構)時,它可能比較有用.
*/
public static native void yield();
/**
* 此方法會引起當前執行線程sleep(臨時停止執行)指定毫秒數.
* 此方法的調用不會引起當前線程放棄任何監聽器(monitor)的所有權(ownership).
* 讓出資源但是並不會釋放對象鎖。
*/
public static native void sleep(long millis) throws InterruptedException;
//millis爲毫秒,nanos爲納秒,1000納秒=1毫秒,其他跟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++;
}
sleep(millis);
}
//初始化一個新線程的方法
//g所屬的線程組、target待執行的目標代碼、name新線程的名字、stackSize用於新線程分配所需堆棧大小
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
//重載初始化方法
//前四個參數同上
//AccessControlContext 用於繼承的訪問控制上下文
//inheritThreadLocals 如果值爲true,從構造線程獲取可繼承的線程局部變量的初始值
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
//如果name爲空,則拋空異常。
if (name == null) {
throw new NullPointerException("name cannot be null");
}
//name不爲空,則將值賦給線程私有name變量
this.name = name;
//將調用該初始化方法的線程,設爲待創建線程的父線程
Thread parent = currentThread();
//獲取系統的安全管理工具
SecurityManager security = System.getSecurityManager();
//如果線程組爲空
if (g == null) {
//首先檢測其是否爲一個小應用
//如果有安全管理,查詢安全管理需要做的工作
if (security != null) {
g = security.getThreadGroup();
}
//如果安全管理在線程所屬父線程組的問題上沒有什麼強制的要求
//線程組將使用父線程的線程組
if (g == null) {
g = parent.getThreadGroup();
}
}
//無論所屬線程組是否顯示傳入,都要進行檢查訪問.
//檢查是否允許調用線程修改線程組參數
g.checkAccess();
//檢查是否有訪問權限
if (security != null) {
//該函數的定義在下面,證明創建當前子類實例能夠忽略安全限制
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
//線程組中的計數方法,
//無論線程啓動與否都需計數,以免破壞其中包含未啓動線程的守護進程線程組。
g.addUnstarted();
//線程私有變量賦值
this.group = g;
this.daemon = parent.isDaemon(); //如果父線程爲守護線程,則此線程也被 設置爲守護線程.
this.priority = parent.getPriority(); //當前線程繼承父線程的優先級
//如果安全管理爲空,或者父線程能夠忽略安全限制調用父線程的getContextClassLoader,通過反射方式獲取上下文類加載器
//否則直接獲取父線程的類加載器
if (security == null || isCCLOverridden(parent.getClass()))
//如果安全管理器存在,且父線程的類加載器不爲null,且它們不相同,且也不是父子關係,則此方法的調用會導致安全管理的方法 checkPermission的調用,用於確定對上下文類加載器的檢索是否被允許.
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
//如果父線程的訪問控制上下文不爲空,則將訪問控制上下文直接賦值給此線程的訪問控制上下文,否則通過訪問控制器獲取上下文
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
//將目標代碼Runnable對象賦給線程私有target變量
this.target = target;
//設置優先級
setPriority(priority);
//如果繼承父線程的ThreadLocal,那麼將父線程的inheritableThreadLocals拷貝給該線程的inheritableThreadLocals變量;
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
//指定存儲的堆棧深度,以防虛擬機關注
this.stackSize = stackSize;
//爲新線程設置線程ID
tid = nextThreadID();
}
//線程不支持淺拷貝,取而代之的是構造一個新的線程
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
//分配一個新的線程對象.此構造器和Thread(ThreadGroup,Runnable,String) 構造器的效果一樣.
//初始化函數參數:(null,null,gname),其中@code gname是新生成的名稱。
//生成名字的格式爲“Thread-”+n,其中n是整數。
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
//作用與上一個構造器基本相同,唯一的不同時多了一個target對象。
//此線程啓動時調用對象的run方法的。如果該參數爲空,則此類的 run方法不起任何作用。
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
//此構造器生成的線程繼承控制訪問上下文.
//此構造器爲非公有方法.
Thread(Runnable target, AccessControlContext acc) {
init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
}
//功能與第一二個公有構造器類似
//group參數,是一個線程組,如果爲空,且當前有一個安全管理器,則該組由SecurityManager類的getThreadGroup方法確定,即SecurityManager.getThreadGroup()。
//如果沒有安全管理器或security manager.getthreadgroup()方法返回空值,則該組被設置爲當前線程的線程組。
//如果當前線程無法在指定的線程組中創建線程則拋出安全異常(SecurityException)
public Thread(ThreadGroup group, Runnable target) {
init(group, target, "Thread-" + nextThreadNum(), 0);
}
//功能同上
//只傳入線程名
public Thread(String name) {
init(null, null, name, 0);
}
//功能同上
//只傳入線程組和線程名
public Thread(ThreadGroup group, String name) {
init(group, null, name, 0);
}
//功能同上
//只傳入目標代碼和線程名
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
public Thread(ThreadGroup group, Runnable target, String name) {
init(group, target, name, 0);
}
/**
* @param stackSize 此值具有平臺依賴性,有的虛擬機中此值可能很大以儘量避免棧溢出;有的虛擬機中則很小;
* 還有的虛擬機直接忽略此值的設置.
* @since 1.4
*/
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
init(group, target, name, stackSize);
}
/**
* 此方法的調用會引起當前線程的執行;JVM會調用此線程的run()方法.
* 結果就是兩個線程可以併發執行:當前線程(從調用的start方法返回)和另一個線程(它在執行run方法).
* 一個線程不可以同時被多次啓動。
* 尤其注意:一個線程執行完成後可能並不會再被重新執行.
*/
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
/**
* 這個方法(start)不會被main方法線程或由虛擬機所創建/設置的“系統”組線程調用。
* 任何向此方法添加的新功能方法在未來都會被添加到虛擬機中.
* 0狀態值代表了NEW的狀態
* 若當前線程狀態不爲新增狀態
* 拋出線程狀態不合法異常。
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
//通知線程組該線程即將啓動,以便將其添加到線程組的線程列表中,並且可以減少組的未開始線程計數器。
group.add(this);
//先初始化啓動狀態變量爲失敗。
boolean started = false;
try {
//調用線程啓動本地方法
start0();
//調用成功後改變啓動狀態變量
started = true;
} finally {
//無論本地方法啓動成功與否都執行下面代碼
try {
if (!started) {
//如果啓動失敗,將調用group的線程啓動失敗方法將該線程置爲啓動失敗
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
//什麼都不必做,如果Start0()拋出了一個throwable那麼,這個throwable會被傳遞到調用堆棧中。
}
}
}
//聲明本地方法Start0()
private native void start0();
//如果這個線程構造時傳入了一個單獨的Runnable run對象,那麼這個Runnable對象的run())方法會被調用
//否則什麼也不做
//Thread的子類應該重寫該方法
@Override
public void run() {
if (target != null) {
target.run();
}
}
//該方法由系統調用,以便在線程實際退出之前爲其提供清理的機會。
private void exit() {
//如果線程組不爲空
if (group != null) {
//那麼將該線程傳入線程組的線程終止方法,銷燬該線程。
group.threadTerminated(this);
//線程銷燬後將線程組置爲空。
group = null;
}
/* Aggressively null out all reference fields: see bug 4006245 */
//積極地清空所有引用字段:參見bug 4006245
target = null;
/* Speed the release of some of these resources */
//加快釋放這些資源的速度
threadLocals = null;
inheritableThreadLocals = null;
inheritedAccessControlContext = null;
blocker = null;
uncaughtExceptionHandler = null;
}
/**
* 強制線程停止執行。
* 如果安裝了安全管理器,則使用this參數調用它的checkaccess方法
* 這可能導致(在當前線程中)引發SecurityException
* 如果此線程與當前線程不同(即,當前線程正在嘗試停止其自身以外的線程),則安
* 全管理器的checkpermission方法(帶有runtimePermission(“stop
* thread”)參數)將被調用。
* 同樣,這可能會導致拋出SecurityException(在當前線程中)
* 這個線程被表示爲該線程無論是操作異常還是拋出了新創建的ThreadDeath對象作爲
* 一個異常都會被強制停止。
* 允許停止尚未啓動的線程。
* 如果線程最終還是啓動了,那麼它將立即終止。
* 應用程序通常不應嘗試捕獲threaddeath異常,除非它必須執行一些特殊的清理操作
* (請注意,threaddeah的拋出會導致try語句的finally子句在線程正式死亡之前被
* 執行)。
* 最頂級的錯誤處理器會對其它未被捕獲類型的異常進行處理,但是如果未處理異常是線
* 程死亡的實例,則不會打印消息或通知應用程序。
*
* @exception SecurityException
* 如果當前線程無法修改此線程。
* @deprecated 這種方法本質上是不安全的。用thread.stop停止線程會導致它解鎖所有
* 已鎖定的監視器(這是未限制的threaddeath異常向堆棧上傳播的自然結
* 果)。以前受這些監視器保護的任何對象都可能處於不一致狀態,則損
* 壞的對象對其他線程都可見,這可能導致它們的任意行爲。
* stop方法的許多用法都應該被替換爲只修改某個變量以指示目標線程應
* 停止運行的代碼。目標線程應定期檢查該變量,如果該變量指示它將停
* 止運行,則應按順序從其run方法返回。如果目標線程等待很長時間(例
* 如,在條件變量上),則應使用interrupt方法中斷等待。
* 更多的信息,參見:<a href="{@docRoot}/../technotes/guides/concurrency/threadPrimitiveDeprecation.html">爲什麼Thread.stop, Thread.suspend and Thread.resume 用已棄用註解。
*/
@Deprecated
public final void stop() {
//獲得系統的安全管理器
SecurityManager security = System.getSecurityManager();
//如果安全管理器爲空
if (security != null) {
//檢查是否允許調用線程修改線程組參數
checkAccess();
//如果所請求的訪問,通過給定的權限,指定的安全策略不允許根據當前有效的方法將拋出一個SecurityException。
if (this != Thread.currentThread()) {
security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
}
}
// A zero status value corresponds to "NEW", it can't change to
// not-NEW because we hold the lock.
// 一個零狀態狀態值對應於"NEW",它不能更改爲"not-NEW",因我我們持有鎖
if (threadStatus != 0) {
//如果線程被掛起,則喚醒線程;否則不執行任何操作
resume(); // Wake up thread if it was suspended; no-op otherwise
}
// The VM can handle all thread states
// 虛擬機可以處理所有線程狀態
stop0(new ThreadDeath());
}
/**
* 拋出UnsupportedOperationException異常
* 此方法最初設計用於強制線程停止並作爲異常拋出給定的@code throwable。
* 它本質上是不安全的(有關詳細信息,請參見@link stop()),而且還生成了
* 目標線程不準備處理的異常。
*/
@Deprecated
public final synchronized void stop(Throwable obj) {
throw new UnsupportedOperationException();
}
/**
* 此方法功能:中斷當前線程.
*
* 除非當前線程在中斷自己(這麼做是允許的),此線程的checkAccess()方法被調用且可能會拋出異常SecurityException
*
* 1.如果當前線程由於wait類型方法、join類型方法或者sleep類型的方法的調用被阻塞,則它的中斷狀態將被清除且會收到一個
* 中斷異常InterruptedException
*
* 2.如果此線程由於java.nio.channels.InterruptibleChannel類中的InterruptibleChannel的I/O操作而被阻塞,
* 則此方法會導致通道被關閉,且線程的中斷狀態會被重置,同時線程會收到一個ClosedByInterruptException.異常
*
* 3.如果此線程由於java.nio.channels.Selector而阻塞,則線程的中斷狀態會被重置,且它將立即從阻塞的selection操作返回,
* 且返回值通常是一個非零值,這就和java.nio.channels.Selector#wakeup的wakeup()方法被調用一樣.
*
* 4.如果前面的條件都不成立,那麼該線程的中斷狀態將被重置.。
*
* 中斷一個處於非活着狀態的線程並不需要產生任何其它影響.
*
* @revised 6.0
* @spec JSR-51
*/
public void interrupt() {
//如果當前線程不等於當前正在執行的線程
//那麼檢查是否允許調用線程修改線程組參數
if (this != Thread.currentThread())
checkAccess();
//對阻塞鎖使用同步機制
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); //只是爲了設定中斷標識位
b.interrupt(this); //中斷當前線程
return;
}
}
interrupt0(); //只是爲了設置中斷標識位
}
/**
* 測試當前線程是否被中斷.
* 線程的中斷狀態會被此方法清除.
* 換句話說,如果此方法兩次調用都能成功,則第二次調用的返回結果爲false(除非在第一次調用完後和第二次調用前,當前線程被再次中斷)
*
* 因爲一個線程在中斷時處於非活動狀態,導致該方法返回false從而導致線程中斷被忽略。
*
* @see #isInterrupted()
* @revised 6.0
*/
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
/**
* 測當前線程是否被中斷.
* 此方法的調用不會影響當前線程的中斷狀態.
*
* 因爲在中斷方法被調用時線程並未處於alive狀態而忽略線程中斷的情況會由於此方法的調用而受到影響.
*
* @see #interrupted()
* @revised 6.0
*/
public boolean isInterrupted() {
return isInterrupted(false);
}
/**
* 測試一些線程是否被中斷.
* 中斷狀態會被重置or並不依賴於之前的中斷清除的值.
*/
private native boolean isInterrupted(boolean ClearInterrupted);
/**
* Throws {@link NoSuchMethodError}.
* 此方法在最開始被設計的目的是:不帶任何清除操作的銷燬一個線程.
* 此方法被調用後線程持有的監聽器依舊處於鎖狀態.
* 然而,此方法從未被實現過.如果被實現,則由於懸掛的問題會帶來死鎖.
* 當目標線程被銷燬時,如果它持有了一個用於保護臨界資源的鎖,那麼會導致此臨界資源再也無法被其它線程使用.
* 如果其它線程嘗試對此資源進行加鎖,就會導致死鎖.這種死鎖通常表現爲"凍結"狀態.
* 所以該方法目前也被棄用
* @throws NoSuchMethodError always
*/
@Deprecated
public void destroy() {
throw new NoSuchMethodError();
}
//測試當前線程是否處於存活狀態.如果一個線程在被啓動後和死亡狀態前都是存活狀態.
public final native boolean isAlive();
/**
* 將一個線程掛起
* 首先,調用此線程的checkaccess方法時不帶參數。這可能導致拋出SecurityException(在當前線程中)。
* 如果線程處於活動狀態,則它將被掛起,並且在恢復之前不會進一步進行處理。
* @exception SecurityException 如果當前線程無法修改此線程。
* @see #checkAccess
* @deprecated 此方法已被棄用,因爲它本身容易死鎖。如果目標線程在掛起時在保護
* 關鍵系統資源的監視器上持有鎖,則在恢復目標線程之前,任何線程都無法訪問此資
* 源。如果要恢復目標線程的線程在調用resume之前嘗試鎖定此監視器,則會導致死
* 鎖。這種死鎖通常表現爲“凍結”的進程。
*/
@Deprecated
public final void suspend() {
//檢查是否允許調用線程修改線程組參數
checkAccess();
//調用本地掛起方法
suspend0();
}
/**
* 恢復一個掛起的線程。
* 首先,調用此線程的checkaccess方法時不帶參數。這可能導致拋出SecurityException(在當前線程中)。
* 如果線程是活動的但掛起的,那麼它將被恢復並允許在執行過程中取得進展。
* @exception SecurityException 如果當前線程無法修改此線程。
* @see #checkAccess
* @see #suspend()
* @deprecated 此方法僅與@link suspend一起使用,
* 該方法已被棄用,因爲它容易死鎖。
*/
@Deprecated
public final void resume() {
checkAccess();
resume0();
}
/**
* 更改此線程的優先級。
* 首先調用此線程的checkaccess方法,不帶參數。這可能導致引發SecurityException
* 否則,該線程的優先級將設置爲指定的新優先級和線程線程組的最大允許優先級中的
* 較小者。
*/
public final void setPriority(int newPriority) {
//聲明線程組變量
ThreadGroup g;
//檢查是否允許調用線程修改線程組參數
checkAccess();
//若新優先級超過限制,則拋出非法參數異常
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
//若獲取線程組不爲空,且新優先級大於線程組的最大優先級
//則將線程組的最大優先級賦給新優先級然後通過本地方法更改線程的優先級。
if((g = getThreadGroup()) != null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}
//返回該線程的優先級
public final int getPriority() {
return priority;
}
/**
* 這是一個同步方法,用於設定線程名
*/
public final synchronized void setName(String name) {
//檢查是否允許調用線程修改線程組參數
checkAccess();
//若參數爲空則拋出空異常
if (name == null) {
throw new NullPointerException("name cannot be null");
}
//將本地名稱變量設置爲新名稱參數
this.name = name;
//若線程狀態不爲新建
if (threadStatus != 0) {
//設置本地姓名變量
setNativeName(name);
}
}
//返回該線程的名稱
public final String getName() {
return name;
}
//返回此線程所屬的線程組。如果此線程已終止(已停止),則此方法返回空值。
public final ThreadGroup getThreadGroup() {
return group;
}
/**
* 返回當前線程的線程組及其子組中活動線程數的估計值。
* 遞歸迭代當前線程線程組中的所有子組。
* 返回的值只是一個估計值,因爲當此方法遍歷內部數據結構時,線程數可能會動態更
* 改,並且可能受某些系統線程的存在影響。
* 此方法主要用於調試和監視目的。
*
* @return 當前線程的線程組和任何其他以當前線程的線程組爲祖先的線程組中活動線
* 程數的估計值。
public static int activeCount() {
return currentThread().getThreadGroup().activeCount();
}
/**
* 將當前線程的線程組及其子組中的每個活動線程複製到指定的數組中。此方法簡單地
* 調用當前線程的線程組的java.lang.ThreadGroup#enumerate(Thread[])的方法
* 應用程序可能會使用activecount# activecount方法來估計數組的大小,
* 但是如果數組太短,無法容納所有線程,則會自動忽略多餘的線程。如果獲取當前線
* 程的線程組及其子組中的每個活動線程至關重要,則調用程序應驗證返回的int值是否
* 嚴格小於tarray的長度。
* 由於該方法固有的競爭條件,建議該方法僅用於調試和監視目的。
*
* @param tarray
* 將線程列表放入其中的數組
*
* @return 放入數組的線程數
*
* @throws SecurityException
*
*/
public static int enumerate(Thread tarray[]) {
return currentThread().getThreadGroup().enumerate(tarray);
}
/**
* 統計此線程中的堆棧幀數。線程必須掛起。
* @return 返回此線程中堆棧幀的數目。
* @exception IllegalThreadStateException 如果這個線程沒有掛起。
* @deprecated 此調用的定義取決於@link suspend,這已被棄用。
* 而且,這個調用的結果從未被很好地定義。
*/
@Deprecated
public native int countStackFrames();
/**
* 最多等待@code millis毫秒,此線程將停止。超時@code 0表示持續等待。
* 此方法在實現上:循環調用以this.isAlive()方法爲條件的wait()方法.
* 當線程終止時notifyAll()方法會被調用.
* 建議應用程序不要在thread實例上使用wait、notify或notifyall方法。
* @param millis
* 等待時間(毫秒)
* @throws IllegalArgumentException
* 如果@code millis的值爲負數
* @throws InterruptedException
* 如果有線程中斷了當前線程。當拋出此異常時,
* 當前線程的“中斷狀態”將被清除。
*/
//加了同步鎖
public final synchronized void join(long millis)
throws InterruptedException {
//獲取當前系統的毫秒值作爲基數
long base = System.currentTimeMillis();
//初始化now變量爲0
long now = 0;
//當入參毫秒值小於0則拋出非法參數異常
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
//如果毫秒值爲0,則當線程處於alive狀態,則循環調用wait(0)方法
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
//毫秒值部位0,如果當前狀態處於alive狀態,循環執行等待時間直到處理時間小於
//等於0跳出循環
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
//等待時間單位爲納秒,其它解釋都和上面方法一樣
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++;
}
join(millis);
}
/**
* 方法功能:等待一直到線程死亡.
* 此方法的調用與調用的行爲完全相同
* @throws InterruptedException
* 如果有線程中斷了當前線程。當拋出此異常時,
* 當前線程的“中斷狀態”將被清除。
*/
public final void join() throws InterruptedException {
join(0);
}
//將當前線程的堆棧跟蹤打印到標準錯誤流。此方法僅用於調試。
public static void dumpStack() {
new Exception("Stack trace").printStackTrace();
}
/**
* 將當前線程設定爲守護線程or用戶線程.
* 當運行的唯一線程都是守護進程線程時,Java虛擬機退出。
* 此方法在start前被調用.
* @param on 如果值爲true,則此線程被設定爲守護線程
*/
public final void setDaemon(boolean on) {
checkAccess();
if (isAlive()) {
throw new IllegalThreadStateException();
}
daemon = on;
}
//查看當前線程是否爲守護線程
public final boolean isDaemon() {
return daemon;
}
/**
* 確定當前運行的線程是否有權利更改此線程.
* 如果有安全管理器,則會將當前線程作爲參數傳入checkAccess()方法.
* 這可能會導致SecurityException異常的拋出.
*/
public final void checkAccess() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkAccess(this);
}
}
//線程轉換爲toString,格式包括 :線程名+優先級+所屬組別
public String toString() {
ThreadGroup group = getThreadGroup();
if (group != null) {
return "Thread[" + getName() + "," + getPriority() + "," +
group.getName() + "]";
} else {
return "Thread[" + getName() + "," + getPriority() + "," +
"" + "]";
}
}
/**
* 此方法返回此線程的上下文類加載器.上下文類加載器由當加載類和資源時使用代碼創建線程的創造者提供.
* 如果通過方法setContextClassLoader進行上下文類加載器的設定,則默認的上下文類加載器爲父線程.
* 原始線程的類加載器通常被設定爲:加載應用的類加載器.
*
* 如果安全管理器存在,且調用者的類加載器不爲null,且它們不相同,且也不是父子關係,則此方法的調用會導致安全管理的SecurityManager#checkPermission(java.security.Permission) checkPermission方法。
* checkPermission的調用,用於確定對上下文類加載器的檢索是否被允許.
*
* 關於註解@CallerSensitive:這個註解是爲了堵住漏洞用的。曾經有黑客通過構造雙重反射來提升權限,原理是當時反射只檢查固定深度的
* 調用者的類,看它有沒有特權.使用CallerSensitive後,getCallerClass不再用固定深度去尋找actual caller(“我”),而是把所
* 有跟反射相關的接口方法都標註上CallerSensitive,搜索時凡看到該註解都直接跳過,這樣就有效解決了這類的黑客問題.
* @since 1.2
*/
@CallerSensitive
public ClassLoader getContextClassLoader() {
if (contextClassLoader == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
ClassLoader.checkClassLoaderPermission(contextClassLoader,
Reflection.getCallerClass());
}
return contextClassLoader;
}
/**
* 設定一個線程的上下文類加載器.上下文類加載器可以在線程被創建時被設定,且允許線程創建者提供合適的類加載器.
*
* 如果存在安全管理器,則它的checkPermission()方法會被調用,用於查看設定上下文類加載器的行爲是否被允許.
* @since 1.2
*/
public void setContextClassLoader(ClassLoader cl) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("setContextClassLoader"));
}
contextClassLoader = cl;
}
/**
* 當且僅當當前線程持有指定對象的監聽器鎖時,返回返回true.
* 這一方法被設計的目的是:用於程序自身去聲明它已經有某個對象的鎖啦.
* @since 1.4
*/
public static native boolean holdsLock(Object obj);
private static final StackTraceElement[] EMPTY_STACK_TRACE
= new StackTraceElement[0];
/**
* 返回表示該線程堆棧轉儲的堆棧跟蹤元素數組。
* 如果線程還沒有start,or雖然start了,但是並沒有被CPU調用過,or線程以及終止了,則返回數組長度爲0.
* 如果返回數組長度非0,則數組中第一個元素(索引爲0)代表了棧的頂部,就是所有調用方法中距離現在時間最近的那個.
* 數組的最後一個元素代表了棧的底部,這是所有調用方法中距離現在時間最遠的那個.
*
* 如果存在安全管理器,且這一線程又不是當前線程,則安全管理器的checkPermission()方法會被調用以查看
*
* 一些虛擬機在某些情況下,可能會在棧跟蹤時遺漏至少一個以上的棧.在極端情況下,虛擬機沒有任何棧跟蹤信息所以返回數組長度爲0.
* @since 1.5
*/
public StackTraceElement[] getStackTrace() {
//如果此線程並不是當前線程
if (this != Thread.currentThread()) {
//檢查getStackTrace許可情況.
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(
SecurityConstants.GET_STACK_TRACE_PERMISSION);
}
//if方法是一種優化,以便我們不會在此處調用虛擬機裏面還沒有開始或者已經死亡的線程.
if (!isAlive()) {
return EMPTY_STACK_TRACE;
}
StackTraceElement[][] stackTraceArray = dumpThreads(new Thread[] {this});
StackTraceElement[] stackTrace = stackTraceArray[0];
//在前面的isAlive檢查中一個活着的線程可能在此時已經終結了,所以此時不會再有棧跟蹤.
if (stackTrace == null) {
stackTrace = EMPTY_STACK_TRACE;
}
return stackTrace;
} else {
//此處並不需要JVM的幫忙.
return (new Exception()).getStackTrace();
}
}
/**
* 返回一個用於所有存活線程的棧跟蹤信息的map.
* map的key是每個線程;value是對應線程的棧跟蹤元素的一個數組.
*
* 當此方法被調用時,可能有些線程正在執行.每一個線程的棧跟蹤信息都代表了線程在某一時刻狀態的快照且每一個棧跟蹤信息
* 會在不同的時間得到.如果虛擬機中某一個線程沒有棧跟蹤信息則其數組長度爲0.
*
* 如果有安全管理器,則安全管理器的checkPermission方法會被調用以檢查是否允許獲取所有線程的棧跟蹤信息.
*
* @since 1.5
*/
public static Map<Thread, StackTraceElement[]> getAllStackTraces() {
//檢查getStackTrace許可情況.
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(
SecurityConstants.GET_STACK_TRACE_PERMISSION);
security.checkPermission(
SecurityConstants.MODIFY_THREADGROUP_PERMISSION);
}
//獲取一個所有線程的列表的一個快照
Thread[] threads = getThreads();
StackTraceElement[][] traces = dumpThreads(threads);
Map<Thread, StackTraceElement[]> m = new HashMap<>(threads.length);
for (int i = 0; i < threads.length; i++) {
StackTraceElement[] stackTrace = traces[i];
if (stackTrace != null) {
m.put(threads[i], stackTrace);
}
// 否則就終止了所以我們不把它放到Map裏
}
return m;
}
//常量:運行時許可
private static final RuntimePermission SUBCLASS_IMPLEMENTATION_PERMISSION =
new RuntimePermission("enableContextClassLoaderOverride");
//子類安全審覈結果的緩存
//在將來如果它出現的話,可以替代ConcurrentReferenceHashMap
private static class Caches {
//子類安全審覈結果的緩存值
static final ConcurrentMap<WeakClassKey,Boolean> subclassAudits =
new ConcurrentHashMap<>();
//審覈子類的弱引用隊列
static final ReferenceQueue<Class<?>> subclassAuditsQueue =
new ReferenceQueue<>();
}
/**
* 證明是否可以在不違反安全約束的情況下構造此(可能是子類)實例:子類不能覆蓋安
* 全敏感,非final類型的方法,否則將檢查“EnableContextClassLoaderOverride”運行時
* 權限。
*/
private static boolean isCCLOverridden(Class<?> cl) {
//如果傳入的時Thread類或子類直接返回false
if (cl == Thread.class)
return false;
//不太清楚這裏的代碼
processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
Boolean result = Caches.subclassAudits.get(key);
if (result == null) {
result = Boolean.valueOf(auditSubclass(cl));
Caches.subclassAudits.putIfAbsent(key, result);
}
return result.booleanValue();
}
// 對給定的子類執行反射檢查,以驗證它是否重寫安全敏感的非final方法。如果子類
// 重寫任何方法,則返回true,否則返回false。
private static boolean auditSubclass(final Class<?> subcl) {
Boolean result = AccessController.doPrivileged(
new PrivilegedAction<Boolean>() {
public Boolean run() {
for (Class<?> cl = subcl;
cl != Thread.class;
cl = cl.getSuperclass())
{
try {
cl.getDeclaredMethod("getContextClassLoader", new Class<?>[0]);
return Boolean.TRUE;
} catch (NoSuchMethodException ex) {
}
try {
Class<?>[] params = {ClassLoader.class};
cl.getDeclaredMethod("setContextClassLoader", params);
return Boolean.TRUE;
} catch (NoSuchMethodException ex) {
}
}
return Boolean.FALSE;
}
}
);
return result.booleanValue();
}
//本地方法,往堆棧壓入線程組,返回堆棧深度數據
private native static StackTraceElement[][] dumpThreads(Thread[] threads);
//本地方法獲取線程組
private native static Thread[] getThreads();
/**
* 返回當前線程的ID.
* 線程ID是一個long類型的正數,在線程被創建時就有的.
* 線程ID在其生命週期內都不會更改且獨一無二.
* 當線程終止時,則此線程的ID會被重複使用.
* @since 1.5
*/
public long getId() {
return tid;
}
/**
* 此枚舉表示線程狀態.線程狀態有如下幾種:
* 1.NEW表示:線程還未開始,只是進行了一些線程創建的初始化操作,但未調用start()方法.
* 2.RUNNABLE表示:線程在JVM裏面處於運行狀態(這裏就緒和運行同屬於運行).
* 3.BLOCKED表示:線程正在等待一個監視器鎖,處於阻塞狀態.
* 4.WAITING表示:一個線程在等待另一個線程的特定操作(通知or中斷),這種等待是無限期的.
* 5.TIMED_WAITING表示:一個線程在等待另一個線程的特定操作,這種等待是有時間限制的.一旦超時則線程自行返回.
* 6.TERMINATED表示:線程已退出.表示線程已經執行完畢.
*
* 線程在某一時刻,只能處於上述6個狀態的某一個.這些狀態值是虛擬機狀態值,因而並不會反映操作系統的線程狀態.
*
* @since 1.5
* @see #getState
*/
public enum State {
//尚未啓動的線程的線程狀態。
NEW,
//線程可以正在運行,也可以處於就緒狀態等待獲得CPU.
//Running爲獲取到了CPU時間片的狀態,人爲增加的一種狀態,不在官方狀態聲明之中
RUNNABLE,
//線程的線程狀態被阻止,正在等待監視器鎖。
//在調用完wait()方法後,處於阻塞狀態的線程正在等待監視器鎖進入同步塊/方法,
//或在調用對象後重新輸入同步塊/方法。爲了進入同步方法(鎖)或重進入同步方法(鎖)
BLOCKED,
/**
* 等待線程的線程狀態。
* 一個線程處於wating狀態,是因爲調用了下面方法中的某一個:
* 1.Object.wait 沒有超時
* 2.Thread.join 沒有超時
* 3.LockSupport.park
*
* 處於等待狀態的線程正在等待另一個線程執行特定操作。
* 其它線程的特定操作包括 :notify(),notifyAll(),join()等.
* 例如,一個在對象上調用了object.wait()的線程正在等待另一個線程在該對象上調
* 用object.notify()或object.notifyAll()。調用thread.join()的線程正在等
* 待指定的線程終止。
*/
WAITING,
/**
* 具有指定等待時間的等待線程的線程狀態。
* 由於使用指定的正等待時間調用以下方法之一,線程處於正等待狀態:
* 1.Thread.sleep()
* 2.Object.wait()
* 3.Thread.join()
* 4.LockSupport.parkNanos()
* 5.LockSupport.parkUntil()
*/
TIMED_WAITING,
//終止線程的線程狀態。
//線程完成了執行
TERMINATED;
}
/**
* 返回此線程的狀態。
* 此方法設計用於監視系統狀態,而不是同步控制。
* @return 這個線程的狀態.
* @since 1.5
public State getState() {
// get current thread state
return sun.misc.VM.toThreadState(threadStatus);
}
// Added in JSR-166
/**
* 由於未捕獲異常而導致線程終止的函數接口處理器.
* 當一個線程由於未捕獲異常而終止時,JVM將會使用getUncaughtExceptionHandler來查詢此線程的UncaughtExceptionHandler,
* 且會調用處理器handler的uncaughtException()方法,將此線程和其異常作爲參數.
*
* 如果一個線程沒有它特定的UncaughtExceptionHandler,則它所屬的線程組對象充當其UncaughtExceptionHandler.
* 如果線程組對象沒有處理異常的指定請求,它可以向前調用getDefaultUncaughtExceptionHandler的默認處理異常的方法.
*
* @see #setDefaultUncaughtExceptionHandler
* @see #setUncaughtExceptionHandler
* @see ThreadGroup#uncaughtException
* @since 1.5
*/
@FunctionalInterface
public interface UncaughtExceptionHandler {
/**
* 由於未捕獲異常而導致線程終止的方法調用.
* 此方法拋出的任何異常都會被JVM忽略.
* @param t the thread
* @param e the exception
*/
void uncaughtException(Thread t, Throwable e);
}
//除非顯式設置,否則爲空
private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
//除非顯式設置,否則爲空
private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
/**
* 設定默認處理器用於處理:由於未捕獲異常而導致的線程終止,且此線程還未定義任何其它的處理器.
*
* 未捕獲異常首先由本線程進行處理,然後由線程所屬的線程組對象處理,最後由默認未捕獲異常處理器進行處理.
* 如果線程未設定明確的未捕獲異常處理器,且線程的線程組(包括父線程組)也未指定,則此時默認處理器的uncaughtException
* 方法會被執行.由於設定了默認的未捕獲異常處理器,則應用能夠更改未捕獲異常的處理方法.
*
* 注意:默認的未捕獲異常處理器不應該經常使用線程的線程組對象,因爲這會引起無限遞歸.
*
* @since 1.5
*/
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(
new RuntimePermission("setDefaultUncaughtExceptionHandler")
);
}
defaultUncaughtExceptionHandler = eh;
}
/**
* 返回當線程因未捕獲的異常而突然終止時調用的默認處理程序。如果返回值爲空,
* 則沒有默認值。
* @since 1.5
* @see #setDefaultUncaughtExceptionHandler
* @return the default uncaught exception handler for all threads
*/
public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){
return defaultUncaughtExceptionHandler;
}
//返回當此線程由於未捕獲的異常而突然終止時調用的處理程序。
//如果該線程沒有顯式設置未捕獲的異常處理程序,則返回該線程的ThreadGroup對象,
//除非該線程已終止,在這種情況下返回空值。
public UncaughtExceptionHandler getUncaughtExceptionHandler() {
return uncaughtExceptionHandler != null ?
uncaughtExceptionHandler : group;
}
/**
* 設定一個由於未捕獲異常而導致線程中斷的處理器.
* 通過設定未捕獲異常處理器,一個線程可以完全控制如何處理未捕獲異常.
* 如果沒有設定未捕獲異常,則線程組對象默認爲其未捕獲異常處理器.
* @since 1.5
*/
public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
checkAccess();
uncaughtExceptionHandler = eh;
}
//將未捕獲異常分發給處理器.這一方法通常被JVM調用.
private void dispatchUncaughtException(Throwable e) {
getUncaughtExceptionHandler().uncaughtException(this, e);
}
//從指定的映射中刪除已在指定引用隊列中排隊的任何鍵。
static void processQueue(ReferenceQueue<Class<?>> queue,
ConcurrentMap<? extends
WeakReference<Class<?>>, ?> map)
{
Reference<? extends Class<?>> ref;
while((ref = queue.poll()) != null) {
map.remove(ref);
}
}
/**
* Weak key for Class objects.
* WeakReference類說明:弱引用對象(JVM四種引用中的弱引用),這並不妨礙它們的引用對象被最終化、定型,然後回收.弱引用最常用於實現規範化映射。
**/
static class WeakClassKey extends WeakReference<Class<?>> {
//用於保存引用的參照hash值,一旦參照明確後用於保持一個持續的hash值.
private final int hash;
//根據給定的對象創建一個weakClassKey,使用給定隊列進行註冊.
WeakClassKey(Class<?> cl, ReferenceQueue<Class<?>> refQueue) {
super(cl, refQueue);
hash = System.identityHashCode(cl);
}
//返回原始引用的參照hash值
@Override
public int hashCode() {
return hash;
}
/**
* 如果給定對象是此相同的weakclassky實例,或者如果尚未清除此對象的引用,
* 如果給定對象是另一個與此對象具有相同的非空引用的weakclassky實例,
* 則返回true。
*/
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj instanceof WeakClassKey) {
Object referent = get();
return (referent != null) &&
(referent == ((WeakClassKey) obj).get());
} else {
return false;
}
}
}
/**
* 以下三個初始未初化的變量專門由java.util.concurrent.ThreadLocalRandom管理.
* 這些變量用在併發代碼中構建高性能的PRNGs,由於存在共享失敗的情況所以我們不能冒險共享.
* 因此,這些變量和註解@Contended是隔離的.
*/
//用於ThreadLocalRandom的當前種子
@sun.misc.Contended("tlr")
long threadLocalRandomSeed;
//探測hash值;如果threadLocalRandomSeed已經初始化了,則其值非0
@sun.misc.Contended("tlr")
int threadLocalRandomProbe;
//隔離ThreadLocalRandom序列的第二個種子
@sun.misc.Contended("tlr")
int threadLocalRandomSecondarySeed;
//一些私有本地輔助方法
private native void setPriority0(int newPriority);
private native void stop0(Object o);
private native void suspend0();
private native void resume0();
private native void interrupt0();
private native void setNativeName(String name);
}
線程狀態及生命週期演化圖
Hotspot源碼路徑hotspot/src/share/vm/classfile/javaClasses.hpp中尋找線程狀態整型值:
線程狀態 | 整型值 | 解釋 |
---|---|---|
新建(NEW) | 0 | 剛剛創建,尚未啓動(未調用start()方法)的線程處於此狀態 |
運行(RUNNABLE) | 5 | Java線程中將就緒(ready)和運行中(running)兩種狀態籠統的稱爲“運行”。 線程對象創建後,其他線程(比如main線程)調用了該對象的start()方法。 該狀態的線程位於可運行線程池中,等待被線程調度選中,獲取CPU的使用權,此時處於就緒狀態。 就緒狀態的線程在獲得CPU時間片後變爲運行中狀態(running)。 |
阻塞(BLOCKED) | 1025 | 表示線程阻塞於鎖。 |
等待(WAITING) | 401 | 進入該狀態的線程需要等待其他線程做出一些特定動作(通知或中斷)。 |
限時等待(TIMED_WAITING) | 417 | 該狀態不同於WAITING,它可以在指定的時間後自行返回。 |
終止(TERMINATED) | 2 | 表示該線程已經執行完畢。 |
等待通知相關方法:
等待/通知的相關方法是任意Java對象都具備的,因爲這些方法被定義在所有對象的超類
java.lang.Object上
方法名稱 | 描述 |
---|---|
notify() | 通知一個在對象上等待的線程,使其從wait()方法返回,而返回的前提是該線程獲取到了對象的鎖1 |
notifyAll() | 通知所有等待在該對象上的對象 |
wait() | 調用該方法的線程進入WAITING狀態,只有等待另外線程的通知或被中斷纔會返回,需要注意,調用wait()後,會釋放對象的鎖 |
wait(long) | 超時等待一段時間,這裏的參數時間是毫秒,也就是等待長達n毫秒,如果沒有通知就超時返回 |
wait(long, int) | 對於超時時間更細粒度的控制,可以達到納秒 |
等待/通知機制,是指一個線程A調用了對象O的wait()方法進入等待狀態,而另一個線程B調用了對象O的notify()或者notifyAll()方法,線程A收到通知後從對象O的wait()方法返回,進而執行後續操作。上述兩個線程通過對象O來完成交互,而對象上的wait()和notify/notifyAll()的關係就如同開關信號一樣,用來完成等待方和通知方之間的交互工作。
- 線程分配到的時間片多少也就決定了線程使用處理器資源的多少,而線程優先級就是決定線程需要多或者少分配一些處理器資源的線程屬性。線程優先級不能作爲程序正確性的依賴,因爲操作系統可以完全不用理會Java線程對於優先級的設定。
- 當線程執行wait()方法之後,線程進入等待狀態。進入等待狀態的線程需要依靠其他線程的通知才能夠返回到運行狀態,而超時等待狀態相當於在等待狀態的基礎上增加了超時限制,也就是超時時間到達時將會返回到運行狀態。當線程調用同步方法時,在沒有獲取到鎖的情況下,線程將會進入到阻塞狀態。線程在執行Runnable的run()方法之後將會進入到終止狀態。
- Java將操作系統中的運行和就緒兩個狀態合併稱爲運行狀態。阻塞狀態是線程阻塞在進入synchronized關鍵字修飾的方法或代碼塊(獲取鎖)時的狀態,但是阻塞在java.concurrent包中Lock接口的線程狀態卻是等待狀態,因爲java.concurrent包中Lock接口對於阻塞的實現均使用了LockSupport類中的相關方法。
- 每當線程調度器有機會選擇新線程時, 它首先選擇具有較高優先級的線程。但是,線程優先級是高度依賴於系統的。當虛擬機依賴於宿主機平臺的線程實現機制時, Java 線程的優先級被映射到宿主機平臺的優先級上, 優先級個數也許更多,也許更少。Windows 有7 個優先級別。一些Java 優先級將映射到相同的操作系統優先級。在Oracle 爲Linux 提供的Java 虛擬機中,線程的優先級被忽略一所有線程具有相同的優先級。