Thread類源碼解析

一.概述

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)換有更深刻的認識:


線程的狀態(tài)轉(zhuǎn)換圖
  1. 創(chuàng)建狀態(tài)(New)
    當用new操作符創(chuàng)建一個新的線程對象時,該線程處于創(chuàng)建狀態(tài),尚未啟動(還未調(diào)用 start() 方法)。
    處于創(chuàng)建狀態(tài)的線程只是一個空的線程對象,系統(tǒng)不為它分配資源。

  2. 可運行狀態(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)度。

  3. 運行狀態(tài)(Running)
    處于可運行狀態(tài)的線程已經(jīng)具備了運行條件,但還沒有分配到CPU,處于線程就緒隊列(盡管是采用隊列形式,事實上,把它稱為可運行池而不是可運行隊列。因為cpu的調(diào)度不一定是按照先進先出的順序來調(diào)度的),等待系統(tǒng)為其分配CPU。
    一旦獲得CPU,線程就進入運行狀態(tài)并自動調(diào)用自己的run方法。

  4. 不可運行狀態(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)。

  5. 死亡狀態(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)建和啟動線程

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容