說來也有些汗顏,搞了幾年java,突然發現居然沒拜讀過java.lang.Thread類源碼,此次特意拿出來曬一曬。本文將剖析Thread類源碼(本文後面源碼所有默認JDK8),並講解一些重要的拓展點。但願對你們能有一些幫助。html
本文講解主幹所有出自源碼和註釋,保證了權威性。(注意:網上,某些書中不少觀點都是錯的,過期的,片面的,因此你們必定要看源碼,重要事情說N遍,看源碼!看源碼!看源碼......)java
在正式學習Thread類中的具體方法以前,咱們先來了解一下線程有哪些狀態,這個將會有助於後面對Thread類中的方法的理解。linux
自JDK5開始,線程包括如下6個狀態,摘自Thread.State:編程
1 /** 2 * A thread state. A thread can be in one of the following states: 3 * <ul> 4 * <li>{@link #NEW}<br> 5 * A thread that has not yet started is in this state. 6 * </li> 7 * <li>{@link #RUNNABLE}<br> 8 * A thread executing in the Java virtual machine is in this state. 9 * </li> 10 * <li>{@link #BLOCKED}<br> 11 * A thread that is blocked waiting for a monitor lock 12 * is in this state. 13 * </li> 14 * <li>{@link #WAITING}<br> 15 * A thread that is waiting indefinitely for another thread to 16 * perform a particular action is in this state. 17 * </li> 18 * <li>{@link #TIMED_WAITING}<br> 19 * A thread that is waiting for another thread to perform an action 20 * for up to a specified waiting time is in this state. 21 * </li> 22 * <li>{@link #TERMINATED}<br> 23 * A thread that has exited is in this state. 24 * </li> 25 * </ul> 26 * 27 * <p> 28 * A thread can be in only one state at a given point in time.----》JVM中的線程必須只能是以上6種狀態的一種。這些狀態是JVM狀態並不能和操做系統線程狀態互相映射。 29 * These states are virtual machine states which do not reflect 30 * any operating system thread states. 31 * 32 * @since 1.5 33 * @see #getState 34 */ 35 public enum State { 36 /** 37 * Thread state for a thread which has not yet started. 38 */ 39 NEW,--->線程剛建立,還未執行(start方法) 40 41 /** 42 * Thread state for a runnable thread. A thread in the runnable 43 * state is executing in the Java virtual machine but it may 44 * be waiting for other resources from the operating system 45 * such as processor. 46 */ 47 RUNNABLE,--->已就緒可運行的狀態。處於此狀態的線程是正在JVM中運行的,但可能在等待操做系統級別的資源,例如CPU時間片 48 49 /** 50 * Thread state for a thread blocked waiting for a monitor lock. 51 * A thread in the blocked state is waiting for a monitor lock 52 * to enter a synchronized block/method or 53 * reenter a synchronized block/method after calling 54 * {@link Object#wait() Object.wait}. 55 */ 56 BLOCKED,--->阻塞等待監視器鎖。處於此狀態的線程正在阻塞等待監視器鎖,以進入一個同步塊/方法,或者在執行完wait()方法後重入同步塊/方法。 57 58 /** 59 * Thread state for a waiting thread. 60 * A thread is in the waiting state due to calling one of the 61 * following methods: 62 * <ul> 63 * <li>{@link Object#wait() Object.wait} with no timeout</li> 64 * <li>{@link #join() Thread.join} with no timeout</li> 65 * <li>{@link LockSupport#park() LockSupport.park}</li> 66 * </ul> 67 * 68 * <p>A thread in the waiting state is waiting for another thread to 69 * perform a particular action. 70 * 71 * For example, a thread that has called <tt>Object.wait()</tt> 72 * on an object is waiting for another thread to call 73 * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on 74 * that object. A thread that has called <tt>Thread.join()</tt> 75 * is waiting for a specified thread to terminate. 76 */ 77 WAITING,--->等待。執行完Object.wait無超時參數操做,或者 Thread.join無超時參數操做(進入等待指定的線程執行結束),或者 LockSupport.park操做後,線程進入等待狀態。
通常在等待狀態的線程在等待其它線程執行特殊操做,例如:等待另其它線程操做Object.notify()喚醒或者Object.notifyAll()喚醒全部。 78 79 /** 80 * Thread state for a waiting thread with a specified waiting time. 81 * A thread is in the timed waiting state due to calling one of 82 * the following methods with a specified positive waiting time: 83 * <ul> 84 * <li>{@link #sleep Thread.sleep}</li> 85 * <li>{@link Object#wait(long) Object.wait} with timeout</li> 86 * <li>{@link #join(long) Thread.join} with timeout</li> 87 * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> 88 * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> 89 * </ul> 90 */ 91 TIMED_WAITING,--->限時等待。Thread.sleep、Object.wait帶超時時間、Thread.join帶超時時間、LockSupport.parkNanos、LockSupport.parkUntil這些操做會時線程進入限時等待。 92 93 /** 94 * Thread state for a terminated thread. 95 * The thread has completed execution. 96 */ 97 TERMINATED;--->終止,線程執行完畢。 98 }
看了源碼6種狀態,不少人會迷惑怎麼沒有Running狀態呢?好吧,請相信源碼,不要混淆操做系統線程狀態和java線程狀態。JVM中的線程必須只能是以上6種狀態的一種!(見上圖枚舉State 註釋中的紅色部分)。緩存
Running實際上是早期操做系統下「單線程進程」的狀態,以下圖:安全
注意:上圖已年久失修,不可參考!!!!併發
好吧,如今是否是以爲三觀被顛覆...app
最新JAVA(JVM)線程狀態轉換以下圖:less
如上圖,可見:RUNNABLE = 正在JVM中運行的(Running)+ 可能在等待操做系統級別的資源(Ready),例如CPU時間片ide
線程建立以後,不會當即進入就緒狀態,由於線程的運行須要一些條件(好比內存資源),只有線程運行須要的全部條件知足了,才進入就緒狀態。
當線程進入就緒狀態後,不表明馬上就能獲取CPU執行時間,也許此時CPU正在執行其餘的事情,所以它要等待。當獲得CPU執行時間以後,線程便真正進入運行狀態。
線程在運行狀態過程當中,可能有多個緣由致使當前線程不繼續運行下去,好比用戶主動讓線程睡眠(睡眠必定的時間以後再從新執行)、用戶主動讓線程等待,或者被同步塊給阻塞,此時就對應着多個狀態:time waiting(睡眠或等待必定的事件)、waiting(等待被喚醒)、blocked(阻塞)。
當因爲忽然中斷或者子任務執行完畢,線程就會被消亡。
老規矩,先看源碼註釋:
/** * A <i>thread</i> is a thread of execution in a program. The Java ---》一個「線程」是在在程序中執行的線程。Java虛擬機容許應用多個線程併發運行。 * Virtual Machine allows an application to have multiple threads of * execution running concurrently. * <p> * Every thread has a priority. Threads with higher priority are--》每一個線程都有優先級,優先級高的先執行。線程多是守護線程或者不是。 * executed in preference to threads with lower priority. Each thread * may or may not also be marked as a daemon. When code running in * some thread creates a new <code>Thread</code> object, the new---》線程的優先級等於建立線程的優先級,當且僅當一個線程是守護線程,建立出來的線程纔是守護線程 * thread has its priority initially set equal to the priority of the * creating thread, and is a daemon thread if and only if the * creating thread is a daemon. * <p> * When a Java Virtual Machine starts up, there is usually a single--》一般JVM啓動,有一個非守護線程做爲主線程。只有當Runtime.exit被調用或者全部非守護線程死亡時(run執行完畢並返回/拋出異常)JVM會中止運行這些線程。 * non-daemon thread (which typically calls the method named * <code>main</code> of some designated class). The Java Virtual * Machine continues to execute threads until either of the following * occurs: * <ul> * <li>The <code>exit</code> method of class <code>Runtime</code> has been * called and the security manager has permitted the exit operation * to take place. * <li>All threads that are not daemon threads have died, either by * returning from the call to the <code>run</code> method or by * throwing an exception that propagates beyond the <code>run</code> * method. * </ul> * <p> * There are two ways to create a new thread of execution. One is to--》兩種建立線程的方法:繼承Thread類/實現Runnable接口 * declare a class to be a subclass of <code>Thread</code>. This * subclass should override the <code>run</code> method of class * <code>Thread</code>. An instance of the subclass can then be * allocated and started. For example, a thread that computes primes * larger than a stated value could be written as follows: * <hr><blockquote><pre> * class PrimeThread extends Thread { * long minPrime; * PrimeThread(long minPrime) { * this.minPrime = minPrime; * } * * public void run() { * // compute primes larger than minPrime * . . . * } * } * </pre></blockquote><hr> * <p> * The following code would then create a thread and start it running: * <blockquote><pre> * PrimeThread p = new PrimeThread(143); * p.start(); * </pre></blockquote> * <p> * The other way to create a thread is to declare a class that * implements the <code>Runnable</code> interface. That class then * implements the <code>run</code> method. An instance of the class can * then be allocated, passed as an argument when creating * <code>Thread</code>, and started. The same example in this other * style looks like the following: * <hr><blockquote><pre> * class PrimeRun implements Runnable { * long minPrime; * PrimeRun(long minPrime) { * this.minPrime = minPrime; * } * * public void run() { * // compute primes larger than minPrime * . . . * } * } * </pre></blockquote><hr> * <p> * The following code would then create a thread and start it running: * <blockquote><pre> * PrimeRun p = new PrimeRun(143); * new Thread(p).start(); * </pre></blockquote> * <p> * Every thread has a name for identification purposes. More than--》每一個線程有本身的名稱用來標識本身。但可能多個線程會重名,若是啓動時沒有建立名字,會自動生成一個。 * one thread may have the same name. If a name is not specified when * a thread is created, a new name is generated for it. * <p> * Unless otherwise noted, passing a {@code null} argument to a constructor * or method in this class will cause a {@link NullPointerException} to be * thrown. * * @author unascribed --》意思是:該代碼第一原做者不是我,但我實在也不知道是誰,就記做無名氏吧(版權意識) * @see Runnable * @see Runtime#exit(int) * @see #run() * @see #stop() * @since JDK1.0 */
Thread類實現了Runnable接口,在Thread類中,
關鍵屬性:
name是表示Thread的名字,能夠經過Thread類的構造器中的參數來指定線程名字,
priority表示線程的優先級(最大值爲10,最小值爲1,默認值爲5),
daemon表示線程是不是守護線程,若是在main線程中建立了一個守護線程,當main方法運行完畢以後,守護線程也會隨着消亡。在JVM中,垃圾收集器線程就是守護線程。
target表示要執行的任務。
group線程羣組
關鍵方法:
如下是關係到線程運行狀態的幾個方法:
1)start
start()用來啓動一個線程,當調用start方法後,系統纔會開啓一個新的線程來執行用戶定義的子任務,在這個過程當中,會爲相應的線程分配須要的資源。
2)run
run()方法是不須要用戶來調用的,當經過start方法啓動一個線程以後,當線程得到了CPU執行時間,便進入run方法體去執行具體的任務。注意,繼承Thread類必須重寫run方法,在run方法中定義具體要執行的任務。
3)sleep
sleep方法有兩個重載版本:
1 public static native void sleep(long millis) throws InterruptedException; 2 3 public static void sleep(long millis, int nanos) throws InterruptedException;
sleep讓線程睡眠,交出CPU,讓CPU去執行其餘的任務。sleep方法不會釋放鎖,也就是說若是當前線程持有對某個對象的鎖,則即便調用sleep方法,其餘線程也沒法訪問這個對象。sleep方法至關於讓線程進入阻塞狀態。
4)yield
調用yield方法會讓當前線程交出CPU權限,讓CPU去執行其餘的線程。它跟sleep方法相似,一樣不會釋放鎖。可是yield不能控制具體的交出CPU的時間,另外,yield方法只能讓擁有相同優先級的線程有獲取CPU執行時間的機會。
注意,調用yield方法並不會讓線程進入阻塞狀態,而是讓線程重回就緒狀態,它只須要等待從新獲取CPU執行時間,這一點是和sleep方法不同的。
5)join
join方法有三個重載版本:
1 join() 2 join(long millis) //參數爲毫秒 3 join(long millis,int nanoseconds) //第一參數爲毫秒,第二個參數爲納秒
能夠看出,當調用thread.join()方法後,main線程會進入等待,而後等待thread執行完以後再繼續執行。
實際上調用join方法是調用了Object的wait方法,這個能夠經過查看源碼得知:
wait方法會讓線程進入阻塞狀態,而且會釋放線程佔有的鎖,並交出CPU執行權限。
6)interrupt
interrupt,中斷。單獨調用interrupt方法可使得處於阻塞狀態的線程拋出一個異常,也就說,它能夠用來中斷一個正處於阻塞狀態的線程;
7)stop
stop方法已是一個廢棄的方法,它是一個不安全的方法。由於調用stop方法會直接終止run方法的調用,而且會拋出一個ThreadDeath錯誤,若是線程持有某個對象鎖的話,會徹底釋放鎖,致使對象狀態不一致。因此stop方法基本是不會被用到的。
8)destroy
destroy方法也是廢棄的方法。基本不會被使用到。
LockSupport類是Java6(JSR166-JUC)引入的一個類,提供了基本的線程同步原語。LockSupport其實是調用了Unsafe類裏的函數,歸結到Unsafe裏,只有兩個函數:
掛起
public native void park(boolean isAbsolute, long time);
喚醒
public native void unpark(Thread jthread);
unpark函數爲線程提供「許可(permit)」,park函數則等待「許可」。這個有點像信號量,可是這個「許可」是不能疊加的,「許可」是一次性的。
好比線程B連續調用了三次unpark函數,當線程A調用park函數就使用掉這個「許可」,若是線程A再次調用park,則進入等待狀態。
注意,unpark函數能夠先於park調用。好比線程B調用unpark函數,給線程A發了一個「許可」,那麼當線程A調用park時,它發現已經有「許可」了,那麼它會立刻再繼續運行。
實際上,park函數即便沒有「許可」,有時也會無理由地返回,這點等下再解析。
park/unpark模型真正解耦了線程之間的同步,線程之間再也不須要一個Object或者其它變量來存儲狀態,再也不須要關心對方的狀態。
咱們從JDK源碼開始看,java.util.concurrent.locks.LookSupport.park()以下:
1 /** 2 * Disables the current thread for thread scheduling purposes unless the 3 * permit is available.--->中止當前線程的調度執行一直到許可可達。 4 * 5 * <p>If the permit is available then it is consumed and the call 6 * returns immediately; otherwise the current thread becomes disabled 7 * for thread scheduling purposes and lies dormant until one of three 8 * things happens: 9 *--->當許可條件知足時,當前線程會當即返回。不然會一直中止線程調度而且假死一直到下面3件事情發生: 10 * <ul> 11 * 12 * <li>Some other thread invokes {@link #unpark unpark} with the 13 * current thread as the target; or 14 *--->1.其它線程調用unpark方法喚醒此線程 15 * <li>Some other thread {@linkplain Thread#interrupt interrupts} 16 * the current thread; or 17 *--->2.其它線程中斷此線程 18 * <li>The call spuriously (that is, for no reason) returns. 19 * </ul> 20 **--->3.此線程未知錯誤返回了 21 * <p>This method does <em>not</em> report which of these caused the 22 * method to return. Callers should re-check the conditions which caused 23 * the thread to park in the first place. Callers may also determine, 24 * for example, the interrupt status of the thread upon return.
*----》該方法不會告知是哪一個緣由致使的返回。調用方須要從新校驗致使線程park的條件。好比中斷狀態。 25 */ 26 public static void park() { 27 UNSAFE.park(false, 0L);//線程調用該方法,線程將一直阻塞直到超時(這裏沒有超時時間爲0),或者是中斷條件出現。 28 }
這裏咱們就簡單看一下park()源碼,目錄:
openjdk-8-src-b132-03_mar_2014\openjdk\hotspot\src\share\vm\runtime\park.cpp
openjdk-8-src-b132-03_mar_2014\openjdk\hotspot\src\share\vm\runtime\park.hpp
openjdk-8-src-b132-03_mar_2014\openjdk\hotspot\src\os\linux\vm\os_linux.cpp
openjdk-8-src-b132-03_mar_2014\openjdk\hotspot\src\os\linux\vm\os_linux.hpp
park.hpp:
1 class Parker : public os::PlatformParker { 2 private: 3 volatile int _counter ; 4 Parker * FreeNext ; 5 JavaThread * AssociatedWith ; // Current association 6 7 public: 8 Parker() : PlatformParker() { 9 _counter = 0 ; 10 FreeNext = NULL ; 11 AssociatedWith = NULL ; 12 } 13 protected: 14 ~Parker() { ShouldNotReachHere(); } 15 public: 16 // For simplicity of interface with Java, all forms of park (indefinite, 17 // relative, and absolute) are multiplexed into one call. 18 void park(bool isAbsolute, jlong time); 19 void unpark(); 20 21 // Lifecycle operators 22 static Parker * Allocate (JavaThread * t) ; 23 static void Release (Parker * e) ; 24 private: 25 static Parker * volatile FreeList ; 26 static volatile int ListLock ; 27 28 };
os_linux.hpp中,PlatformParker:
1 class PlatformParker : public CHeapObj<mtInternal> { 2 protected: 3 enum { 4 REL_INDEX = 0, 5 ABS_INDEX = 1 6 }; 7 int _cur_index; // which cond is in use: -1, 0, 1 8 pthread_mutex_t _mutex [1] ; 9 pthread_cond_t _cond [2] ; // one for relative times and one for abs. 10 11 public: // TODO-FIXME: make dtor private 12 ~PlatformParker() { guarantee (0, "invariant") ; } 13 14 public: 15 PlatformParker() { 16 int status; 17 status = pthread_cond_init (&_cond[REL_INDEX], os::Linux::condAttr()); 18 assert_status(status == 0, status, "cond_init rel"); 19 status = pthread_cond_init (&_cond[ABS_INDEX], NULL); 20 assert_status(status == 0, status, "cond_init abs"); 21 status = pthread_mutex_init (_mutex, NULL); 22 assert_status(status == 0, status, "mutex_init"); 23 _cur_index = -1; // mark as unused 初始化時-1未使用 24 } 25 };
能夠看到Parker類實際上用Posix的mutex,condition來實現的。
在Parker類裏的_counter字段,就是用來記錄所謂的「許可」的。
park()源碼實現,爲了保證源碼的完整性,就直接在源碼上註釋原理了。
1 void Parker::park(bool isAbsolute, jlong time) { 2 // Ideally we'd do something useful while spinning, such 3 // as calling unpackTime(). 4 5 // Optional fast-path check: 6 // Return immediately if a permit is available. 7 // We depend on Atomic::xchg() having full barrier semantics 8 // since we are doing a lock-free update to _counter. 9 if (Atomic::xchg(0, &_counter) > 0) return;//先嚐試可否直接拿到「許可」,即_counter>0時,若是成功,則把_counter設置爲0,並返回: 10 11 Thread* thread = Thread::current(); 12 assert(thread->is_Java_thread(), "Must be JavaThread"); 13 JavaThread *jt = (JavaThread *)thread; 14 15 // Optional optimization -- avoid state transitions if there's an interrupt pending. 16 // Check interrupt before trying to wait 17 if (Thread::is_interrupted(thread, false)) { 18 return; 19 } 20 21 // Next, demultiplex/decode time arguments 22 timespec absTime; 23 if (time < 0 || (isAbsolute && time == 0) ) { // don't wait at all 24 return; 25 } 26 if (time > 0) { 27 unpackTime(&absTime, isAbsolute, time); 28 } 29 30 31 // Enter safepoint region 32 // Beware of deadlocks such as 6317397. 33 // The per-thread Parker:: mutex is a classic leaf-lock. 34 // In particular a thread must never block on the Threads_lock while 35 // holding the Parker:: mutex. If safepoints are pending both the 36 // the ThreadBlockInVM() CTOR and DTOR may grab Threads_lock. 37 ThreadBlockInVM tbivm(jt);//若是不成功,則構造一個ThreadBlockInVM, 38 39 // Don't wait if cannot get lock since interference arises from 40 // unblocking. Also. check interrupt before trying wait 41 if (Thread::is_interrupted(thread, false) || pthread_mutex_trylock(_mutex) != 0) { 42 return; 43 } 44 45 int status ; 46 if (_counter > 0) { // no wait needed而後檢查_counter是否是>0,若是是,則把_counter設置爲0,unlock mutex並返回: 47 _counter = 0; 48 status = pthread_mutex_unlock(_mutex); 49 assert (status == 0, "invariant") ; 50 // Paranoia to ensure our locked and lock-free paths interact 51 // correctly with each other and Java-level accesses. 52 OrderAccess::fence(); 53 return; 54 } 55 56 #ifdef ASSERT 57 // Don't catch signals while blocked; let the running threads have the signals. 58 // (This allows a debugger to break into the running thread.) 59 sigset_t oldsigs; 60 sigset_t* allowdebug_blocked = os::Linux::allowdebug_blocked_signals(); 61 pthread_sigmask(SIG_BLOCK, allowdebug_blocked, &oldsigs); 62 #endif 63 64 OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */); 65 jt->set_suspend_equivalent(); 66 // cleared by handle_special_suspend_equivalent_condition() or java_suspend_self() 67 68 assert(_cur_index == -1, "invariant"); 69 if (time == 0) { 70 _cur_index = REL_INDEX; // arbitrary choice when not timed 71 status = pthread_cond_wait (&_cond[_cur_index], _mutex) ; 72 } else { 73 _cur_index = isAbsolute ? ABS_INDEX : REL_INDEX; 74 status = os::Linux::safe_cond_timedwait (&_cond[_cur_index], _mutex, &absTime) ; 75 if (status != 0 && WorkAroundNPTLTimedWaitHang) { 76 pthread_cond_destroy (&_cond[_cur_index]) ; 77 pthread_cond_init (&_cond[_cur_index], isAbsolute ? NULL : os::Linux::condAttr()); 78 } 79 } 80 _cur_index = -1; 81 assert_status(status == 0 || status == EINTR || 82 status == ETIME || status == ETIMEDOUT, 83 status, "cond_timedwait"); 84 85 #ifdef ASSERT 86 pthread_sigmask(SIG_SETMASK, &oldsigs, NULL); 87 #endif 88 89 _counter = 0 ; 90 status = pthread_mutex_unlock(_mutex) ; 91 assert_status(status == 0, status, "invariant") ; 92 // Paranoia to ensure our locked and lock-free paths interact 93 // correctly with each other and Java-level accesses. 94 OrderAccess::fence(); 95 96 // If externally suspended while waiting, re-suspend 97 if (jt->handle_special_suspend_equivalent_condition()) { 98 jt->java_suspend_self(); 99 } 100 }
unpark()源碼實現
1 void Parker::unpark() { 2 int s, status ; 3 status = pthread_mutex_lock(_mutex);//互斥鎖加鎖 4 assert (status == 0, "invariant") ; 5 s = _counter;//保存初始counter 6 _counter = 1;//置1 7 if (s < 1) {//若是本來爲0 8 // thread might be parked線程可能被掛起 9 if (_cur_index != -1) { 10 // thread is definitely parked 11 if (WorkAroundNPTLTimedWaitHang) { 12 status = pthread_cond_signal (&_cond[_cur_index]);//喚醒在park中等待的線程 13 assert (status == 0, "invariant"); 14 status = pthread_mutex_unlock(_mutex);//釋放鎖 15 assert (status == 0, "invariant"); 16 } else { 17 status = pthread_mutex_unlock(_mutex);//釋放鎖 18 assert (status == 0, "invariant"); 19 status = pthread_cond_signal (&_cond[_cur_index]);//喚醒在park中等待的線程 20 assert (status == 0, "invariant"); 21 } 22 } else { 23 pthread_mutex_unlock(_mutex);//釋放鎖 24 assert (status == 0, "invariant") ; 25 } 26 } else {//若是本來爲1,釋放鎖 27 pthread_mutex_unlock(_mutex); 28 assert (status == 0, "invariant") ; 29 } 30 }
Caches-->WeakClassKey-->WeakReference
1 /** cache of subclass security audit results */ 2 /* Replace with ConcurrentReferenceHashMap when/if it appears in a future 3 * release */ 4 private static class Caches { 5 /** cache of subclass security audit results */ 6 static final ConcurrentMap<WeakClassKey,Boolean> subclassAudits = 7 new ConcurrentHashMap<>(); 8 9 /** queue for WeakReferences to audited subclasses */ 10 static final ReferenceQueue<Class<?>> subclassAuditsQueue = 11 new ReferenceQueue<>(); 12 }
Caches類中包含了兩個成員subclassAudits和subclasseAuditsQueue:
subclassAudits——該成員屬性提供了一個哈希表緩存,該緩存的鍵類型爲java.lang.Thread.WeakClassKey,注意看它的值類型是一個java.lang.Boolean類型的,從其代碼註釋能夠知道這個哈希表緩存中保存的是全部子類的代碼執行安全性檢測結果;
subclassAuditsQueue——該成員屬性定義了一個「Queue隊列」,保存了已經審覈過的子類弱引用
static class WeakClassKey extends WeakReference<Class<?>>關於弱引用WeakReference,飛機票:Java中關於WeakReference和WeakHashMap的理解
參考
《JAVA高併發程序設計》電子工業出版社