寫在開頭
在寫完上一篇文章《Java面試必考題之執行緒的生命週期,結合原始碼,透徹講解!》後,本以為這個小知識點就總結完了。
但剛剛吃晚飯時,突然想到了多年前自己面試時的親身經歷,決定再回來補充一個小知識點!
記得是一個週末去面試Java後端開發工程師崗位
,面試官針對Java多執行緒進行了狂轟亂炸般的考問,什麼執行緒建立的方式、執行緒的狀態、各狀態間的切換、如果保證執行緒安全、各種鎖的區別,如何使用等等,因為有好好背八股文,所以七七八八的也答上來了,但最後面試官問了一個現在看來很簡單,但當時根本不知道的問題,他先是問了我,看過Thread的原始碼沒,我毫不猶豫的回答看過,緊接著他問:
執行緒在呼叫了一次start啟動後,再呼叫一次可以不?如果執行緒執行完,同樣再呼叫一次start又會怎麼樣?
這個問題拋給你們,請問該如何作答呢?
執行緒的啟動
我們知道雖然很多八股文面試題中說Java建立執行緒的方式有3種、4種,或者更多種,但實際上真正可以建立一個執行緒的只有new Thread().start();
【程式碼示例1】
public class Test {
public static void main(String[] args) {
Thread thread = new Thread(() -> {});
System.out.println(thread.getName()+":"+thread.getState());
thread.start();
System.out.println(thread.getName()+":"+thread.getState());
}
}
輸出:
Thread-0:NEW
Thread-0:RUNNABLE
建立一個Thread,這時執行緒處於NEW狀態,這時呼叫start()方法,會讓執行緒進入到RUNNABLE狀態。
RUNNABLE的執行緒呼叫start
在上面測試程式碼的基礎上,我們再次呼叫start()方法。
【程式碼示例2】
public class Test {
public static void main(String[] args) {
Thread thread = new Thread(() -> {});
System.out.println(thread.getName()+":"+thread.getState());
//第一次呼叫start
thread.start();
System.out.println(thread.getName()+":"+thread.getState());
//第二次呼叫start
thread.start();
System.out.println(thread.getName()+":"+thread.getState());
}
}
輸出:
Thread-0:NEW
Thread-0:RUNNABLE
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:708)
at com.javabuild.server.pojo.Test.main(Test.java:17)
第二次呼叫時,程式碼丟擲IllegalThreadStateException異常。
這是為什麼呢?我們跟進start原始碼中一探究竟!
【原始碼解析1】
// 使用synchronized關鍵字保證這個方法是執行緒安全的
public synchronized void start() {
// threadStatus != 0 表示這個執行緒已經被啟動過或已經結束了
// 如果試圖再次啟動這個執行緒,就會丟擲IllegalThreadStateException異常
if (threadStatus != 0)
throw new IllegalThreadStateException();
// 將這個執行緒新增到當前執行緒的執行緒組中
group.add(this);
// 宣告一個變數,用於記錄執行緒是否啟動成功
boolean started = false;
try {
// 使用native方法啟動這個執行緒
start0();
// 如果沒有丟擲異常,那麼started被設為true,表示執行緒啟動成功
started = true;
} finally {
// 在finally語句塊中,無論try語句塊中的程式碼是否丟擲異常,都會執行
try {
// 如果執行緒沒有啟動成功,就從執行緒組中移除這個執行緒
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
// 如果在移除執行緒的過程中發生了異常,我們選擇忽略這個異常
}
}
}
這裡有個threadStatus,若它不等於0表示執行緒已經啟動或結束,直接拋IllegalThreadStateException異常,我們在start原始碼中打上斷點,從第一次start中跟入進去,發現此時沒有報異常。
此時的threadStatus=0,執行緒狀態為NEW,斷點繼續向下走時,走到native方法start0()時,threadStatus=5,執行緒狀態為RUNNABLE。此時,我們從第二個start中進入斷點。
這時threadStatus=5,滿足不等於0條件,丟擲IllegalThreadStateException異常!
TERMINATED的執行緒呼叫start
終止狀態下的執行緒,情況和RUNNABLE類似!
【程式碼示例3】
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {});
thread.start();
Thread.sleep(1000);
System.out.println(thread.getName()+":"+thread.getState());
thread.start();
System.out.println(thread.getName()+":"+thread.getState());
}
}
輸出:
Thread-0:TERMINATED
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:708)
at com.javabuild.server.pojo.Test.main(Test.java:17)
這時同樣也滿足不等於0條件,丟擲IllegalThreadStateException異常!
我們其實可以跟入到state的原始碼中,看一看執行緒幾種狀態設定的邏輯。
【原始碼解析2】
// Thread.getState方法原始碼:
public State getState() {
// get current thread state
return sun.misc.VM.toThreadState(threadStatus);
}
// sun.misc.VM 原始碼:
// 如果執行緒的狀態值和4做位與操作結果不為0,執行緒處於RUNNABLE狀態。
// 如果執行緒的狀態值和1024做位與操作結果不為0,執行緒處於BLOCKED狀態。
// 如果執行緒的狀態值和16做位與操作結果不為0,執行緒處於WAITING狀態。
// 如果執行緒的狀態值和32做位與操作結果不為0,執行緒處於TIMED_WAITING狀態。
// 如果執行緒的狀態值和2做位與操作結果不為0,執行緒處於TERMINATED狀態。
// 最後,如果執行緒的狀態值和1做位與操作結果為0,執行緒處於NEW狀態,否則執行緒處於RUNNABLE狀態。
public static State toThreadState(int var0) {
if ((var0 & 4) != 0) {
return State.RUNNABLE;
} else if ((var0 & 1024) != 0) {
return State.BLOCKED;
} else if ((var0 & 16) != 0) {
return State.WAITING;
} else if ((var0 & 32) != 0) {
return State.TIMED_WAITING;
} else if ((var0 & 2) != 0) {
return State.TERMINATED;
} else {
return (var0 & 1) == 0 ? State.NEW : State.RUNNABLE;
}
}
總結
OK,今天就講這麼多啦,其實現在回頭看看,這僅是一個簡單且微小的細節而已,但對於剛準備步入職場的我來說,卻是一個難題,今天寫出來,除了和大家分享一下Java執行緒中的小細節外,更多的是希望正在準備面試的小夥伴們,能夠心細,多看原始碼,多問自己為什麼?並去追尋答案,Java開發不可淺嘗輒止。
結尾彩蛋
如果本篇部落格對您有一定的幫助,大家記得留言+點贊+收藏呀。原創不易,轉載請聯絡Build哥!
如果您想與Build哥的關係更近一步,還可以關注“JavaBuild888”,在這裡除了看到《Java成長計劃》系列博文,還有提升工作效率的小筆記、讀書心得、大廠面經、人生感悟等等,歡迎您的加入!