1. 程式人生 > >【Java8原始碼分析】執行緒-Thread類的全面剖析

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

一、基本知識

(1)執行緒特性

  • 每個執行緒均有優先順序
  • 執行緒能被標記為守護執行緒
  • 每個執行緒均分配一個name

(2)建立執行緒的方法

  • 繼承Thread類,並重現run方法
// 繼承Thread類
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();
  • 建立Thread類,並傳入構造引數runnable
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();

二、執行緒狀態

    public enum State {
        NEW,
        RUNNABLE,
        BLOCKED,
        WAITING,
        TIMED_WAITING,
        TERMINATED;
    }
  • NEW 狀態是指執行緒剛建立, 尚未啟動
  • RUNNABLE 狀態是執行緒正在正常執行中, 當然可能會有某種耗時計算/IO等待的操作/CPU時間片切換等, 這個狀態下發生的等待一般是其他系統資源, 而不是鎖, Sleep等
  • BLOCKED 這個狀態下, 是在多個執行緒有同步操作的場景, 比如正在等待另一個執行緒的synchronized 塊的執行釋放, 或者可重入的 synchronized塊裡別人呼叫wait() 方法, 也就是這裡是執行緒在等待進入臨界區
  • WAITING 這個狀態下是指執行緒擁有了某個鎖之後, 呼叫了他的wait方法, 等待其他執行緒/鎖擁有者呼叫 notify / notifyAll 一遍該執行緒可以繼續下一步操作, 這裡要區分 BLOCKED 和 WATING 的區別, 一個是在臨界點外面等待進入, 一個是在理解點裡面wait等待別人notify, 執行緒呼叫了join方法 join了另外的執行緒的時候, 也會進入WAITING狀態, 等待被他join的執行緒執行結束
  • TIMED_WAITING 這個狀態就是有限的(時間限制)的WAITING, 一般出現在呼叫wait(long), join(long)等情況下, 另外一個執行緒sleep後, 也會進入TIMED_WAITING狀態
  • TERMINATED 這個狀態下表示 該執行緒的run方法已經執行完畢了, 基本上就等於死亡了(當時如果執行緒被持久持有, 可能不會被回收)

java層次的狀態轉換圖



作業系統層次的狀態轉換圖



三、基本屬性

// Thread本身也是繼承了Runnable介面
public class Thread implements Runnable {

    private volatile char  name[];
    private int            priority;
    private Thread         threadQ;
    private long           eetop;

    private boolean     single_step;
    private boolean     daemon = false;

    // 虛擬機器狀態
    private boolean     stillborn = false;

    // 實際的執行緒任務
    private Runnable target;

    private ThreadGroup group;
    private ClassLoader contextClassLoader;
    private AccessControlContext inheritedAccessControlContext;

    // 所有初始化執行緒的數目
    private static int threadInitNumber;
    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }

    // 這是為ThreadLocal類維護的一些變數
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

    private long stackSize;
    private long nativeParkEventPointer;

    // 執行緒id相關
    private long tid;
    private static long threadSeqNumber;
    private static synchronized long nextThreadID() {
        return ++threadSeqNumber;
    }

    // 執行緒狀態
    private volatile int threadStatus = 0;


    volatile Object parkBlocker;
    private volatile Interruptible blocker;
    private final Object blockerLock = new Object();
    void blockedOn(Interruptible b) {
        synchronized (blockerLock) {
            blocker = b;
        }
    }

    // java中的執行緒總共分了10個優先順序
    // 最小優先順序為1,最大為10,預設為5
    public final static int MIN_PRIORITY = 1;
    public final static int NORM_PRIORITY = 5;
    public final static int MAX_PRIORITY = 10;
}

四、建構函式

    // 最主要的輔助建構函式,所有的建構函式均呼叫init函式
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name.toCharArray();

        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        // 子執行緒繼承父執行緒的優先順序和守護屬性
        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();
        this.target = target;
        setPriority(priority);
        if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        // 獲取唯一的執行緒id,此函式為synchronize
        tid = nextThreadID();
    }

    // 所有的建構函式本質上都是呼叫init方法
    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
    Thread(Runnable target, AccessControlContext acc) {
        init(null, target, "Thread-" + nextThreadNum(), 0, acc);
    // 省略許多建構函式

五、主要方法

    // 啟動一個執行緒
    public synchronized void start() {
        // 執行緒不能重複start
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        group.add(this);

        boolean started = false;
        try {
            // 未native方法
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {

            }
        }
    }

    private native void start0();

    // Thread也實現了Runnable介面
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

    // 由系統呼叫,可以使Thread在銷燬前釋放資源
    private void exit() {
        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) {
                // 只是設定了中斷標誌位
                interrupt0();
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }

    public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

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

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

    // 一些靜態native方法,由jvm實現
    public static native Thread currentThread();
    public static native void yield();
    public static native void sleep(long millis) throws InterruptedException;

    // 還有一些已經不推薦使用的方法
    @Deprecated
    public final void stop() { }
    @Deprecated
    public final void stop() { }
    @Deprecated
    public void destroy() {
        throw new NoSuchMethodError();
    }
    @Deprecated
    public final void suspend() {
        checkAccess();
        suspend0();
    }
    @Deprecated
    public final void resume() {
        checkAccess();
        resume0();
    }

六、總結

(1)執行緒複用

像執行緒池類高效的原因在於,執行緒池中的執行緒在完成任務後,不會銷燬,而且快取起來,每當使用者請求一個執行緒處理任務時,執行緒池可以利用快取的空閒執行緒來處理使用者任務,這樣避免了執行緒建立銷燬帶來的開銷。

在Thread類中有一個Runnable target的域,只需將target替換成新的Runnable即可。

(2)wait()和notify/notifyAll()方法

wait()方法

  • 執行緒進入WAITING狀態,並且釋放掉它所佔有的“鎖標誌”,從而使別的執行緒有機會搶佔該鎖,等待其他執行緒呼叫“鎖標誌“物件的notify或notifyAll方法恢復
  • wait方法是一個本地方法,其底層是通過一個叫做監視器鎖的物件來完成的,所以呼叫wait方式時必須獲取到monitor物件的所有權即通過Synchronized關鍵字,否則丟擲IllegalMonitorStateException異常

notify/notifyAll()方法

  • 在同一物件上去呼叫notify/notifyAll方法,就可以喚醒對應物件monitor上等待的執行緒了。notify和notifyAll的區別在於前者只能喚醒monitor上的一個執行緒,對其他執行緒沒有影響,而notifyAll則喚醒所有的執行緒

(3)sleep/yield/join方法解析

sleep

  • sleep方法的作用是讓當前執行緒暫停指定的時間(毫秒)
  • wait方法依賴於同步,而sleep方法可以直接呼叫
  • sleep方法只是暫時讓出CPU的執行權,並不釋放鎖。而wait方法則需要釋放鎖

yield

  • yield方法的作用是暫停當前執行緒,以便其他執行緒有機會執行,不過不能指定暫停的時間,並且也不能保證當前執行緒馬上停止
  • yield只能使同優先順序或更高優先順序的執行緒有執行的機會

join

  • 等待呼叫join方法的執行緒結束,再繼續執行。如:t.join(),主要用於等待t執行緒執行結束
  • 作用是父執行緒等待子執行緒執行完成後再執行,換句話說就是將非同步執行的執行緒合併為同步的執行緒

(4)不推薦使用方法解釋

suspend()和resume()

  • 這兩個方法是配套使用的,suspend()是暫停執行緒,但並不釋放資源,容易造成死鎖情況

stop()

  • 因為呼叫stop會使執行緒釋放所有的鎖,導致不安全情況,在呼叫stop時候,由鎖保護的臨界區可能處於狀態不一致的情況,這不一致狀態將暴露給其他執行緒
  • 推薦的做法是,維護一個狀態變數,當執行緒需要停止時更改這一狀態變數,該執行緒應檢查這一狀態變數,看該執行緒是否應該終止了

(5)關於interrupt()中斷函式

  • 其實呼叫這個函式並不是真的中斷執行緒,這個函式只是將Thread中的interrupt標誌設定為true,使用者需自行檢測這一變數,停止執行緒,這種做法避免了stop帶來的問題

(6)更深入學習

Thread類中有許多native方法,更深入的學習後續還需研究研究jvm的原始碼。

相關推薦

Java8原始碼分析執行-Thread全面剖析

一、基本知識 (1)執行緒特性 每個執行緒均有優先順序 執行緒能被標記為守護執行緒 每個執行緒均分配一個name (2)建立執行緒的方法 繼承Thread類,並重現run方法 // 繼承Thread類 class PrimeThread e

Java8原始碼分析執行-ThreadLocal的全面剖析

一、背景 ThreadLocal類顧名思義就是,申明為ThreadLocal的變數,對於不同執行緒來說都是獨立的。 下面是一個例子: public class Test { public static void main(String[] a

Java8原始碼分析併發包-AtomicInteger

AtomicInteger類是實現了原子操作的Integer,以往對於保證int、double、float等基礎型別的運算原子性,需要採用加鎖的方式。但是為了一個簡單的運算操作採用鎖,在多執行緒競爭嚴重的情況下,會導致效能降低,所以在java1.5推出了Atom

Java8原始碼分析NIO包-FileChannel

1 概述 Java NIO 由以下幾個核心部分組成: Buffer Channel Selectors 相關類的使用方法可以參考Java NIO 系列教程,寫的通俗易懂。 本文主要從原始碼方面分析一下Channel類。

Java8原始碼分析NIO包-Selector選擇器

1 概述 Java NIO 由以下幾個核心部分組成: Buffer Channel Selectors 相關類的使用方法可以參考Java NIO 系列教程,寫的通俗易懂。 本文主要從原始碼方面分析一下Selector選擇器。關

Java8原始碼分析併發包-ConcurrentHashMap(一)

一、CAS原理簡介 Java8中,ConcurrentHashMap摒棄了Segment的概念,而是啟用了一種全新的方式實現:利用CAS演算法。它沿用了HashMap的思想,底層依然由“陣列”+連結串列+紅黑樹的方式實現。 那什麼CAS演算法呢?以前採用鎖的

Java8原始碼分析集合框架-HashMap

一、HashMap的儲存結構 總共有兩種儲存類 // 1. 雜湊衝突時採用連結串列法的類,一個雜湊桶多於8個元素改為TreeNode static class Node<K,V> implements Map.Entry<K,V> /

Java8原始碼分析併發包-Semaphore

Semaphore是訊號量,它的作用是控制多個允許。 打個比方,一個博物館有容納1000人的能力(Semaphore的允許是1000),假如1500人同時來參館,只有1000人能獲得允許入館(獲得鎖),其餘的500則需排隊(阻塞),直到館內的人離開(釋

java併發程式設計執行池原理分析及ThreadPoolExecutor原始碼實現

執行緒池簡介:  多執行緒技術主要解決處理器單元內多個執行緒執行的問題,它可以顯著減少處理器單元的閒置時間,增加處理器單元的吞吐能力。         假設一個伺服器完成一項任務所需時間為:T1 建立執行緒時間,T2 線上程中執行任務的時間,T3 銷燬執行緒時間。    

go原始碼分析go原始碼之slice原始碼分析

Go 語言切片是對陣列的抽象。 Go 陣列的長度不可改變,與陣列相比切片的長度是不固定的,可以追加元素,在追加時可能使切片的容量增大。 len() 和 cap() 函式     切片是可索引的,並且可以由 len() 方法獲取長度。    

go原始碼分析go原始碼之list原始碼分析

本文針對go 1.11版本,路徑src/container/list/list.go 資料結構 Element結構體 Value 前驅 後繼 // Element is an element of a linked list. type Element st

java原始碼分析Map中的hash演算法分析

全網把Map中的hash()分析的最透徹的文章,別無二家。 2018年05月09日 09:08:08 閱讀數:957 你知道HashMap中hash方法的具體實現嗎?你知道HashTable、ConcurrentHashMap中hash方法

Java虛擬機器執行安全與鎖優化

執行緒安全與鎖優化 絕對執行緒安全 相對執行緒安全 執行緒安全的實現方式 互斥同步 非阻塞同步 鎖優化 參考 絕對執行緒安全 當多個執行緒訪問一個物件時,如果不用考慮這些執行緒在執行時環境

spring原始碼分析IOC容器初始化(二)

前言:在【spring原始碼分析】IOC容器初始化(一)中已經分析了匯入bean階段,本篇接著分析bean解析階段。 1.解析bean程式呼叫鏈 同樣,先給出解析bean的程式呼叫鏈: 根據程式呼叫鏈,整理出在解析bean過程中主要涉及的類和相關方法。 2.解析bean原始碼分

NCNN原始碼分析1.基本資料型別

對於NCNN而言,核心在於網路的前向推理過程(Inference),其主要資料型別為mat,該資料型別以類的形式定義在src/mat.h中,其中包含了mat的建構函式、解構函式、常見的運算過程。 #if

Go 原始碼分析從 sort.go 看排序演算法的工程實踐

go version go1.11 darwin/amd64file: src/sort/sort.go 排序演算法有很多種類,比如快排、堆排、插入排序等。各種排序演算法各有其優劣性,在實際生產過程中用到的排序演算法(或者說 Sort 函式)通常是由幾種排序演算法組

深入原始碼分析Java執行池的實現原理

程式的執行,其本質上,是對系統資源(CPU、記憶體、磁碟、網路等等)的使用。如何高效的使用這些資源是我們程式設計優化演進的一個方向。今天說的執行緒池就是一種對CPU利用的優化手段。 網上有不少介紹如何使用執行緒池的文章,那我想說點什麼呢?我希望通過學習執行緒池原理,明白所有

PHP7原始碼分析如何理解PHP虛擬機器(一)

順風車運營研發團隊 李樂 1.從物理機說起 虛擬機器也是計算機,設計思想和物理機有很多相似之處; 1.1馮諾依曼體系結構 馮·諾依曼是當之無愧的數字計算機之父,當前計算機都採用的是馮諾依曼體系結構;設計思想主要包含以下幾個方面: 指令和資料不加區別混合儲存在同一個儲

Mybatis原始碼分析13-記一次PageHelper reasonable引數使用不當造成的死迴圈

問題描述及原因 使用Mybatis PageHelper外掛做了表的分頁查詢,要求查詢符合某一條件的所有記錄做處理,就寫了一個迭代器在while迴圈裡對每條記錄做處理,直到符合條件的記錄都處理完程式返回。程式碼如下 public class ReconPaymentI

Tomcat9原始碼分析元件與框架概述

1 元件與框架介紹 Server:代表整個Catalina Servlet容器,可以包含一個或多個Service Service:包含Connector和Container的集合,Service用適當的Connector接收使用者的請求,