1. 程式人生 > >Thread類源碼剖析

Thread類源碼剖析

reat ssi com thread類 最新 rate which 不知道 title

目錄

1.引子

2.JVM線程狀態

3.Thread常用方法

4.拓展點


一、引子

說來也有些汗顏,搞了幾年java,忽然發現竟然沒拜讀過java.lang.Thread類源碼,這次特地拿出來曬一曬。本文將剖析Thread類源碼(本文後面源碼全部默認JDK8),並講解一些重要的拓展點。希望對大家能有一些幫助。

本文講解主幹全部出自源碼和註釋,保證了權威性。(註意:網上,某些書中很多觀點都是錯的,過時的,片面的,所以大家一定要看源碼,重要事情說N遍,看源碼!看源碼!看源碼......)

二、JVM線程狀態

在正式學習Thread類中的具體方法之前,我們先來了解一下線程有哪些狀態,這個將會有助於後面對Thread類中的方法的理解。

自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.sleepObject.wait帶超時時間、Thread.join帶超時時間、LockSupport.parkNanosLockSupport.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其實是早期操作系統下“單線程進程”的狀態,如下圖:

技術分享

註意:上圖已年久失修,不可參考!!!!

好吧,現在是不是覺得三觀被顛覆...

最新JAVA(JVM)線程狀態轉換如下圖

技術分享

如上圖,可見:RUNNABLE = 正在JVM中運行的(Running)+ 可能在等待操作系統級別的資源(Read),例如CPU時間片

  線程創建之後,不會立即進入就緒狀態,因為線程的運行需要一些條件(比如內存資源),只有線程運行需要的所有條件滿足了,才進入就緒狀態。

  當線程進入就緒狀態後,不代表立刻就能獲取CPU執行時間,也許此時CPU正在執行其他的事情,因此它要等待。當得到CPU執行時間之後,線程便真正進入運行狀態。

  線程在運行狀態過程中,可能有多個原因導致當前線程不繼續運行下去,比如用戶主動讓線程睡眠(睡眠一定的時間之後再重新執行)、用戶主動讓線程等待,或者被同步塊給阻塞,此時就對應著多個狀態:time waiting(睡眠或等待一定的事件)、waiting(等待被喚醒)、blocked(阻塞)。

  當由於突然中斷或者子任務執行完畢,線程就會被消亡。

三.Thread類中的方法

老規矩,先看源碼註釋:

/**
 * 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方法也是廢棄的方法。基本不會被使用到。

四、

  

  


參考

《JAVA高並發程序設計》電子工業出版社

Java並發編程:Thread類的使用

Thread類源碼剖析