JAVA中多線程的操做對於初學者而言是比較難理解的,其實聯想到底層操做系統時咱們可能會稍微明白些,對於程序而言最終都是硬件上運行二進制指令,然而,這些又太過底層,今天來看一下JAVA中的線程,淺析JDK源碼中的Thread類,以後能幫助咱們更好的處理線程問題java
JDK版本號:1.8.0_171
在Thread註釋中能夠看到大佬對其進行的解釋:緩存
Thread就是程序中一個線程的執行.JVM容許一個應用中多個線程併發執行每一個線程都有優先級.高優先級線程優先於低優先級線程執行
每一個線程均可以(不能夠)被標記爲守護線程
當線程中的run()方法代碼裏面又建立了一個新的線程對象時,新建立的線程優先級和父線程優先級同樣
當且僅當父線程爲守護線程時,新建立的線程纔會是守護線程安全當JVM啓動時,一般會有惟一的一個非守護線程(這一線程用於調用指定類的main()方法)
JVM會持續執行線程直到下面某一個狀況發生爲止:
1.類運行時exit()方法被調用且安全機制容許此exit()方法的調用.
2.全部非守護類型的線程均已經終止,或者run()方法調用返回或者在run()方法外部拋出了一些可傳播性的異常.多線程
能夠聯想下JVM的啓動過程,從main方法啓動,能夠本身寫代碼查看下線程狀況併發
Thread註釋類上清楚的寫明瞭線程的兩種實現方式:app
示例以下,相信各位讀者應該很是熟悉了jvm
public static void main(String[] args) { // 1.繼承Thread類 ExtendsThread extendsThread = new ExtendsThread("test1"); extendsThread.start(); // 2.實現Runnable接口 ImplementsRunnable implementsRunnable = new ImplementsRunnable("test2"); // 實現Runnable接口的類不能單獨使用,最終仍是要依賴Thread Thread implementsRunnableThread = new Thread(implementsRunnable); implementsRunnableThread.start(); } static class ExtendsThread extends Thread{ private String name; public ExtendsThread(String name) { this.name = name; } @Override public void run() { System.out.println("ExtendsThread is " + name); } } static class ImplementsRunnable implements Runnable{ private String name; public ImplementsRunnable(String name) { this.name = name; } @Override public void run() { System.out.println("ImplementsRunnable is " + name); } }
從上面的實現中咱們能看到最終都須要實現Runnable接口,也就是實現run方法,Thread自己也是實現了Runnable接口(可參考源碼部分)ide
先看下Runnable接口實現:函數
public interface Runnable { public abstract void run(); }
很是簡單,就一個run方法,也就意味着咱們只要將自定義代碼邏輯在run方法中實現便可,咱們在日常的線程實現中最簡單的使用方式也就是如上面線程實現的方式,因此咱們最終須要了解的部分仍是在於Thread,接下來就看看本文的重點Thread工具
public class Thread implements Runnable
先了解下static塊的部分,這裏在類首次加載時執行,主要做用就是將C/C++中的方法映射到Java中的native方法,實現方法命名的解耦
通俗點說就是咱們在以後調用native方法時,jvm會直接去調用本地系統方法完成操做,不會再去查找這個方法在哪,再去連接等等,至關於先進行註冊,以後就直接操做使用,由於涉及到底層c語言,這裏不過多深刻解釋
/* Make sure registerNatives is the first thing <clinit> does. */ private static native void registerNatives(); static { registerNatives(); }
Thread內部實現了一些接口和類,下面來一一進行說明
未捕獲異常處理器,在線程因爲未捕獲的異常終止時,JVM會進行一些處理,處理流程以下:
整個調用處理過程可參考下列源碼部分:
// 內部異常處理接口 public interface UncaughtExceptionHandler { void uncaughtException(Thread t, Throwable e); } public UncaughtExceptionHandler getUncaughtExceptionHandler() { // 返回handle自己或線程組(實現了這個接口) return uncaughtExceptionHandler != null ? uncaughtExceptionHandler : group; } // ThreadGroup中代碼邏輯 public void uncaughtException(Thread t, Throwable e) { if (parent != null) { parent.uncaughtException(t, e); } else { Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler(); if (ueh != null) { // 默認handle非空則調用 ueh.uncaughtException(t, e); } else if (!(e instanceof ThreadDeath)) { // 未設置基本都是這裏進行打印堆棧信息 System.err.print("Exception in thread \"" + t.getName() + "\" "); e.printStackTrace(System.err); } } }
可參考下列測試代碼理解:
public static void main(String[] args) { // 實現UncaughtExceptionHandler接口 Thread.UncaughtExceptionHandler handler = (t, e) -> { System.out.println("test:" + t.getName()); e.printStackTrace(); }; // 設置默認UncaughtExceptionHandler // Thread.setDefaultUncaughtExceptionHandler(handler); ExtendsThread extendsThread = new ExtendsThread("test"); // 設置UncaughtExceptionHandler extendsThread.setUncaughtExceptionHandler(handler); extendsThread.start(); } static class ExtendsThread extends Thread { private String name; public ExtendsThread(String name) { this.name = name; } @Override public void run() { int a = 1 / 0; System.out.println(a); } }
從這個接口咱們能夠知道對於線程異常中止咱們實際上是能夠進行一些後續操做的,經過實現UncaughtExceptionHandler接口來進行線程異常後續處理操做,不過我還沒見過有什麼源碼中用過,通常都是直接catch中處理,這裏就先了解下便可
咱們能夠看到WeakClassKey這個內部類繼承了WeakReference,而WeakClassKey被Caches所使用,從名字咱們也能明白其部分含義,本地緩存,WeakClassKey是弱引用相關類,至於弱引用的使用你們能夠自行Google,這裏很少說,若是你看過《深刻理解Java虛擬機》,應該多少有點了解
subclassAudits提供了一個哈希表緩存,該緩存的鍵類型爲java.lang.Thread.WeakClassKey,注意看它的值類型是一個java.lang.Boolean類型的,從其代碼註釋能夠知道這個哈希表緩存中保存的是全部子類的代碼執行安全性檢測結果
subclassAuditsQueue定義了一個Queue隊列,保存已經審覈過的子類弱引用
/** cache of subclass security audit results */ /* Replace with ConcurrentReferenceHashMap when/if it appears in a future * release */ private static class Caches { /** cache of subclass security audit results */ // 緩存安全檢查結果 static final ConcurrentMap<WeakClassKey,Boolean> subclassAudits = new ConcurrentHashMap<>(); // 隊列 /** queue for WeakReferences to audited subclasses */ static final ReferenceQueue<Class<?>> subclassAuditsQueue = new ReferenceQueue<>(); } /** * Weak key for Class objects. **/ static class WeakClassKey extends WeakReference<Class<?>> { /** * saved value of the referent's identity hash code, to maintain * a consistent hash code after the referent has been cleared */ private final int hash; /** * Create a new WeakClassKey to the given object, registered * with a queue. */ WeakClassKey(Class<?> cl, ReferenceQueue<Class<?>> refQueue) { super(cl, refQueue); hash = System.identityHashCode(cl); } /** * Returns the identity hash code of the original referent. */ @Override public int hashCode() { return hash; } /** * Returns true if the given object is this identical * WeakClassKey instance, or, if this object's referent has not * been cleared, if the given object is another WeakClassKey * instance with the identical non-null referent as this one. */ @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj instanceof WeakClassKey) { Object referent = get(); return (referent != null) && (referent == ((WeakClassKey) obj).get()); } else { return false; } } }
在源碼中咱們能夠看到主要在isCCLOverridden
中使用到了,這裏直接看下這個方法的源碼,註釋上也寫明瞭,驗證明例(多是子類)沒有違反安全約束能夠被構建,子類不能重寫安全相關的非final方法,不然須要檢查enableContextClassLoaderOverride運行權,主要是進行安全檢查的,簡單理解就好
/** * Verifies that this (possibly subclass) instance can be constructed * without violating security constraints: the subclass must not override * security-sensitive non-final methods, or else the * "enableContextClassLoaderOverride" RuntimePermission is checked. */ private static boolean isCCLOverridden(Class<?> cl) { if (cl == Thread.class) return false; processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits); // 生成key WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue); // 從緩存查找 Boolean result = Caches.subclassAudits.get(key); if (result == null) { result = Boolean.valueOf(auditSubclass(cl)); Caches.subclassAudits.putIfAbsent(key, result); } // 返回結果 return result.booleanValue(); }
線程狀態枚舉類,代表線程處於生命週期中的哪一個階段,線程在任意時刻只會處於下列其中一種狀態,只反映JVM中的線程狀態,不是反映操做系統線程狀態
public enum State { /** * 初始態 * 線程建立完畢還未啓動,未調用start方法 */ NEW, /** * 包含兩種狀態 * 1.就緒態 * 2.運行態 */ RUNNABLE, /** * 阻塞態 * synchronized會致使線程進入blocked狀態 */ BLOCKED, /** * * 等待態 * 3種狀況調用後會致使線程處於這個狀態: * 1.Object.wait * 2.Thread.join * 3.LockSupport.park * * 等待態的線程會等待其餘線程執行特定的操做 * * 例如一個線程調用了Object.wait以後進入等待態,另外一個線程調用Object.notify或Object.notifyAll能夠將其喚醒,被喚醒的線程須要獲取對象的鎖才能恢復執行 * 調用Thread.join會等待指定的線程終止 */ WAITING, /** * * 超時等待態 * 線程等待指定的時間再執行 * 5種狀況調用後會致使線程處於這個狀態: * 1.Thread.sleep * 2.Object.wait 傳入等待時長 * 3.Thread.join 傳入等待時長 * 4.LockSupport.parkNanos * 5.LockSupport.parkUntil */ TIMED_WAITING, /** * 終止態 * 線程執行完畢 */ TERMINATED; }
線程狀態轉化可參考下圖理解
在Java中線程分爲6種狀態,上面枚舉類也已經說明了,這裏稍微詳細點說明下每種狀態的含義:
初始態(NEW):
運行態(RUNNABLE):
運行態在Java中包括就緒態和運行態
1.就緒態:
2.運行態:
阻塞態(BLOCKED)
等待態(WAITING)
超時等待態(TIMED_WAITING)
終止態(TERMINATED)
其中有幾點須要注意的:
以上對Thread進行了簡單的介紹說明,對於Thread的狀態轉換須要多理解理解,寫些代碼debug或者經過jdk工具觀察下jvm的線程狀態仍是頗有必要的
以上內容若有問題歡迎指出,筆者驗證後將及時修正,謝謝