最近精讀Netty原始碼,讀到NioEventLoop部分的時候,發現對Java執行緒&執行緒池有些概念還有困惑, 所以深入總結一下
執行緒建立
Java執行緒建立主要有三種方式:繼承Thread類、實現Runable介面、實現Callable介面
只有通過呼叫Thread.start()
方法才會真正建立一個執行緒, 呼叫Thread.run()
並不會
當呼叫執行緒關心任務執行結果時,我們應選擇實現Callable介面的方式建立執行緒
-
繼承方式實現建立執行緒
@Test public void testCreate_1() { Thread t = new Thread() { @Override public void run() { System.out.println(Thread.currentThread().getName()); throw new RuntimeException(); } }; t.start(); t.run(); }
-
實現Runnable介面的方式建立執行緒,這種方式呼叫執行緒無法感知任務執行緒執行結果(是否執行、成功或者異常)
@Test public void testCreate_2() { Thread t = new Thread(() -> System.out.println(Thread.currentThread().getName())); t.start(); }
-
實現Callable介面,呼叫執行緒通過FutureTask物件獲取執行結果(返回值或者異常)
@Test public void testCreate_3() throws ExecutionException, InterruptedException { FutureTask<Integer> task = new FutureTask<>(() -> { throw new RuntimeException(); }); new Thread(task).start(); System.out.println(task.get()); }
執行緒的狀態
我們知道Java執行緒是使用系統核心執行緒實現, 所以先來簡單回顧下系統核心執行緒的狀態
核心執行緒的狀態
- Ready狀態:當前執行緒已經就緒,等待系統排程
- Running狀態:當Ready狀態的執行緒分配到時間片後進入該狀態
- Blocking狀態:執行中的執行緒因為其他資源未就緒進入該狀態
Java執行緒的狀態
- NEW: 例項化一個Thread物件後未呼叫start方法前都是該狀態
- RUNNABLE: JVM裡面的可執行狀態,對應核心執行緒的Ready或者Running狀態。所以該狀態下執行緒不一定在在執行,有可能在等待排程
- WAITING: 等待狀態,需要其他執行緒喚醒後才能重新進入RUNNABLE狀態
- TIMED_WAITING:超時等待,等待一定的時間或者被其他執行緒喚醒之後可再進入RUNNABLE狀態
- BLOCKED:阻塞狀態,特指等待進入synchronized同步塊的狀態(獲取監視器鎖)
- Termination:終態
只有待獲取監視器鎖時才是阻塞狀態,獲取Java語言實現的鎖(ReentrantLock等)是等待狀態。二者的區別在於監視器鎖的實現依賴核心變數。
異常處理
假設一段業務邏輯沒有考慮執行時異常, 而執行時異常又剛好發生了,那麼對應的執行緒就會直接崩潰。所以多執行緒環境下為了讓程式更加健壯穩定, 我們需要捕獲異常。
-
將整個業務邏輯加上異常捕獲(當然程式碼就不是很優雅)
@Test public void testExceptionHandle_1() { new Thread(() -> { try { //business code int a = 1, b = 0; a = a / b; } catch (Throwable th) { //log } }).start(); }
-
使用FutureTask非同步回掉處理異常(更加優雅,業務邏輯和異常處理邏輯分離)
@Test public void testExceptionHandle_2() { //業務邏輯 FutureTask<Integer> ft = new FutureTask<>(() -> { //business code int a = 1, b = 0; a = a / b; return a; }); Thread t = new Thread(() -> { ft.run(); handleResult(ft); }); t.start(); } //異常處理邏輯 private void handleResult(FutureTask<Integer> ft) { try { System.out.println("the result is " + ft.get()); } catch (InterruptedException e) { //log or ... e.printStackTrace(); } catch (ExecutionException e) { //log or ... e.printStackTrace(); } }
中斷
Java中斷是一種執行緒間通訊手段。比如A執行緒給B執行緒傳送一箇中斷訊號,B執行緒收到這個訊號,可以處理也可以不處理。
- 中斷相關的API
void thread.interrupt();//例項方法-中斷執行緒(執行緒的中斷標識位置為1)
boolean thread.isInterrupted();//執行緒是否中斷 & 不清除中斷標識
static boolean Thread.interrupted();//當前執行緒是否中斷 & 清除中斷標識
-
例項
執行緒t_1每次迴圈會判斷當前執行緒的中斷狀態,如果當前執行緒已經被中斷(中斷標識位為1)就直接返回;
整個通訊過程:主執行緒把t_1執行緒的中斷標識位置為1,t_1獲取到中斷標識位為1, 然後結束迴圈。
@Test public void testInterrupt() throws InterruptedException { Thread t_1 = new Thread(() -> { int i = 0; while (true) { boolean isInterrupt = Thread.interrupted(); if (isInterrupt) { System.out.println("i am interrupt, return"); return; } //business code if (i++ % 10000 == 0) { System.out.println(i); } } }); t_1.start(); for (int i = 0; i < 100000; i++) { ; } t_1.interrupt(); }
其他
-
守護執行緒
當JVM中的所有的使用者執行緒都退出後守護執行緒也會退出
-
優先順序
執行緒的優先順序越高,越有可能更快的執行或者獲得更多的可執行時間單元。但是Java執行緒的優先順序只是參考,依賴於具體的實現
-
thread.join()
呼叫執行緒進入WAITING狀態,直到thread執行緒終止
-
thread.yield()
當前執行緒讓出cpu資源,仍處於RUNNABLE狀態