Java多執行緒之Thread原始碼分析

玩毛線的包子發表於2018-10-19

一:Thread是什麼

執行緒Thread是程式中的一個實體,是被系統獨立排程和分派的基本單位,執行緒自己不擁有系統資源,只擁有一點在執行中必不可少的資源,但它可與同屬一個程式的其它執行緒共享程式所擁有的全部資源。 Java程式執行時,會啟動一個JVM程式,JVM尋找到程式的入口main(),會建立一個主執行緒執行。一個java程式至少有一個程式,一個程式至少有一個執行緒。

二:執行緒的建立與使用

  1. 繼承Thread,重寫run方法
    class MyThread extends Thread {
        @Override
        public void run() {
            // do something
            super.run();
        }
    }
    
    MyThread myThread = new MyThread();
    myThread.start();
複製程式碼
  1. 實現Runnable介面
    class MyRunnable implements Runnable {
        @Override
        public void run() {
            // do something
        }
    }
    
    Thread thread = new Thread(new MyRunnable());
    thread.start();
複製程式碼

三:執行緒的狀態

  • 執行緒的狀態標誌
    public enum State {
        NEW,
        RUNNABLE,
        BLOCKED,
        WAITING,
        TIMED_WAITING,
        TERMINATED;
    }
複製程式碼
  1. NEW 執行緒剛剛建立,尚未啟動
  2. RUNNABLE 執行緒正在執行
  3. BLOCKED 執行緒堵塞狀態,等待同步塊的鎖的釋放,如果獲得鎖,就會自動進入執行狀態
  4. WAITING 執行緒處於等待狀態,需要被其他執行緒喚醒(notify、notifyAll方法的呼叫)。執行緒在呼叫了join之後,也會進入等待狀態,直到被join的執行緒執行結束才會被喚醒
  5. TIMED_WAITING 有限時間的等待,一般出現在呼叫sleep(time),join(time),sleep(time)後
  6. TERMINATED 執行緒執行結束
  • 執行緒的狀態轉換

Java多執行緒之Thread原始碼分析

四:執行緒的基本屬性

private volatile String name; // 執行緒名稱
private int         priority; // 優先順序,1~10
private boolean     daemon = false; // 是否是守護執行緒
private Runnable target; // 實際被執行的物件
ThreadLocal.ThreadLocalMap threadLocals = null; // 當前執行緒的資料
private long tid; // 執行緒id
複製程式碼

五:執行緒建立初始化

    private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
        // 獲取當前執行緒,給新建立的執行緒設定執行緒組
        Thread parent = currentThread();
        if (g == null) {
            g = parent.getThreadGroup();
        }
        // ThreadGroup nUnstartedThreads++;
        g.addUnstarted();
        this.group = g;

        this.target = target;
        this.priority = parent.getPriority(); // 使用父執行緒的優先順序
        this.daemon = parent.isDaemon();  // 使用父執行緒的屬性,是否是守護執行緒
        setName(name);

        init2(parent);

        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;
        tid = nextThreadID();
    }
複製程式碼

六:主要方法

    public synchronized void start() {
        // 判斷狀態,只能啟動一次
        if (threadStatus != 0 || started)
            throw new IllegalThreadStateException();
        group.add(this);

        started = false;
        try {
            nativeCreate(this, stackSize, daemon);
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
            }
        }
    }
    
    // 執行方法,target是實際可執行的Runnable物件
    public void run() {
        if (target != null) {
            target.run();
        }
    }
    
    // 退出
    private void exit() {
        // 將當前執行緒衝ThreadGroup裡面移除
        if (group != null) {
            group.threadTerminated(this);
            group = null;
        }
        target = null;
        threadLocals = null;
        inheritableThreadLocals = null;
        inheritedAccessControlContext = null;
        blocker = null;
        uncaughtExceptionHandler = null;
    }
    
    // 執行緒中斷
    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                nativeInterrupt();
                b.interrupt(this);
                return;
            }
        }
        nativeInterrupt();
    }
    
    public final void join(long millis) throws InterruptedException {
        synchronized(lock) {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                lock.wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                lock.wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
        }
    }
    
    public static native void yield();
複製程式碼

小結:

  1. 什麼是守護執行緒?

守護執行緒與普通執行緒的唯一區別是:當JVM中所有的執行緒都是守護執行緒的時候,JVM就可以退出了;如果還有一個或以上的非守護執行緒則不會退出。(以上是針對正常退出,呼叫System.exit則必定會退出)

建立守護執行緒,thread.setDaemon(true);必須在start()之前呼叫。

參考:守護執行緒與普通執行緒

  1. ThreadLocal

  2. sleep
    sleep使當前執行緒暫停,並沒有釋放鎖,經過傳入的time時間後自動繼續執行。

  3. yield
    暫停當前執行緒,讓其它優先順序大於等於該執行緒的執行緒有機會先執行,不過不能保證該執行緒能夠立即停止。

  4. join
    當前執行緒暫停,等待插入的執行緒執行完畢之後繼續執行。

  5. interrupt
    執行緒中斷,只是設定了一箇中斷標誌,線上程中使用者自己獲取這個標誌然後自己退出。


參考: 【Java8原始碼分析】執行緒-Thread類的全面剖析

相關文章