《java 核心技術》這本書真的不錯,知識點很全面,翻譯質量也還不錯,本系列博文是對該書中併發章節的一個總結。html
官方解釋:線程是操做系統可以進行運算調度的最小單位,包含於進程之中,是進程中的實際運做單位。也就是說線程是代碼運行的載體,咱們所編寫的代碼都是在線程上跑的,以一個最簡單的 hellowWorld 爲例:java
public class Main { public static void main(String[] args) { System.out.println("Hello World!"); System.out.println("當前線程名爲:"+Thread.currentThread().getName()); System.out.println("當前線程id爲:"+Thread.currentThread().getId()); } }
<!-- more -->git
結果爲:github
Hello World! 當前線程名爲:main 當前線程id爲:1
在程序運行時默認會建立一個主線程來執行代碼,線程名爲:main,線程 id 爲 1多線程
顧名思義就是多個線程同時運行,提升程序執行速度。單個線程一次只能作一件事,想要提升執行效率有兩種途徑:併發
建立線程有兩種方法dom
不推薦本方式來建立線程,緣由顯而易見:java 不支持多繼承,若是繼承了 Thread 類就不能再繼承其餘類了。異步
使用繼承方式建立線程代碼以下:ide
public class CustomThreadExtendThread extends Thread{ @Override public void run() { String threadName = Thread.currentThread().getName(); long threadId = Thread.currentThread().getId(); System.out.println("建立線程名爲:"+threadName+",id爲:"+threadId); } public static void main(String[] args){ Thread thread1 = new CustomThreadExtendThread(); Thread thread2 = new CustomThreadExtendThread(); thread1.start(); thread2.start(); } }
實現接口來建立線程是目前推薦的一種方式,緣由也很簡單:一個類能夠實現多個接口。實現 Runnable 接口並不影響實現類再去實現其餘接口。學習
使用實現接口方式建立線程代碼以下:
public class CustomThreadImplementInterface implements Runnable { @Override public void run() { Thread.currentThread().setName(((Double) Math.random()).toString()); String threadName = Thread.currentThread().getName(); long threadId = Thread.currentThread().getId(); System.out.println("建立線程名爲:" + threadName + ",id爲:" + threadId); } public static void main(String[] args) { Thread thread1 = new Thread(new CustomThreadImplementInterface()); Thread thread2 = new Thread(new CustomThreadExtendThread()); thread1.start(); thread2.start(); //使用lambda表達式,讓建立線程更簡單 new Thread(() -> { System.out.println("建立了一個新線程"); }).start(); } }
經過查看 Thread 源碼能夠看到 Thread 類也是 Runnable 接口的一個實現類。
PS:後續代碼所有使用 runnable 建立線程
上面只是演示了線程的建立,如今來詳細瞭解線程的狀態。在 java 規範中,線程能夠有如下 6 種狀態:
當使用 new 操做符建立一個線程時,如 new Thread(r),線程還未開始運行,就屬於新建立狀態。
一旦調用 Thread 類的 start 方法,線程就處於可運行狀態。
爲何要叫可運行狀態?
由於 Java 的規範中並無將正在 CPU 上運行定義爲一個單獨的狀態。所以處於可運行狀態的線程可能正在運行,也可能沒有運行,取決於 CPU 的調度策略。
當線程處於阻塞或等待狀態時,不運行任何代碼且消耗最少的資源。直到從新運行。有以下幾種途徑讓線程進入阻塞或等待狀態:
線程可由如下兩種辦法進入終止狀態:
注意: 調用線程的 stop 方法也能夠終止線程,可是這個方法已經被棄用,最好不要使用。
線程有各類屬性:優先級,守護線程,線程組以及處理未捕獲異常處理器。
java 中,每一個線程都有一個優先級。默認狀況下,線程繼承父線程優先級。也能夠調用setPriority
方法指定優先級。優先級範圍:1(MIN_PRIORITY)-10(MAX_PRIORITY).NORM_PRIORITY 爲 5,這些常量定義在 Thread 類中.
注意: 線程優先級時高度依賴於系統的,所以當 java 線程優先級映射到宿主機平臺的優先級時,優先級個數可能會變少或者變成 0.好比,Windows 中有 7 個優先級,java 線程映射時部分優先級將會映射到相同的操做系統優先級上。Oracle 爲 Linux 編寫的 java 虛擬機中,忽略了線程的優先級,全部 java 線程都有相同的優先級。不要編寫依賴優先級的代碼。
經過調用Thread.setDaemon(true)
將一個線程轉換爲守護線程。守護線程惟一的用戶是爲其餘線程提供服務,好比計時線程,定時發送計時信號給其餘線程。所以當虛擬機中只有守護線程時,虛擬機就會關閉退出。不要在守護線程中訪問任何資源,處理任何業務邏輯
線程的 run 方法不能拋出任何受查異常,非受查異常會致使線程終止,除了 try/catch 捕獲異常外,還能夠經過未捕獲異常處理器來處理異常。異常處理器須要實現Thread.UncaughtExceptionHandler
接口。
可使用線程示例的setUncaughtExceptionHandler()
方法爲某個線程設置處理器,也可以使用Thread.setDefaultUncaughtExceptionHandler()
爲全部線程設置默認處理器,代碼以下:
public class CustomExceptionHandler implements Thread.UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("捕獲到線程"+t.getName()+",異常:" + e.getMessage()); e.printStackTrace(); } public static void main(String[] args) { Thread.setDefaultUncaughtExceptionHandler(new CustomExceptionHandler()); new Thread(() -> { throw new RuntimeException("test"); }).start(); } }
若是不設置默認處理器且不爲獨立的線程設置處理器,那麼該線程的處理器就爲該線程的線程組對象--ThreadGroup(由於線程組對象實現了Thread.UncaughtExceptionHandler
接口)。
本篇所用所有代碼:github
本篇原創發佈於:https://www.tapme.top/blog/detail/2019-04-08-20-52
原文出處:https://www.cnblogs.com/wuyoucao/p/11100889.html