我們都知道,Java中構造線程的方式有兩種,第一種是繼承Thread類,然後覆寫run方法;第二種是實現Runnable接口,然後實現run方法。但是最終啓動的時候都是通過Thread對象的start方法啓動的。那麼既然邏輯寫在了run方法中,啓動的時候爲什麼調用start方法呢?
1.JDK Thread類的start方法源碼解析
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
通過查看源碼可以看到,在start方法中調用了start0這個方法,這個方法的實現如下:
private native void start0();
該方法是native方法,表示由JDK通過JNI來調用的。但是run方法何時調用的呢?
通過查看官方文檔,可以看到說明,start0的底層實現中是調用了run方法的,只不過是通過C++實現的,感興趣的可以翻一下對應的代碼。
通過上述start方法的源碼,可以總結出以下幾個點:
- (1) 當線程被構造完成之後,有個狀態變量會記錄線程的狀態,就是上述的threadStatus,初始值在類中聲明的爲0;
- (2) 調用start方法啓動線程之後,threadStatus的值會被改變。如果再次重複調用start方法啓動同一個線程時,會拋出IllegalThreadStateException異常;
- (3) thread線程啓動之後,會被加入到一個線程組ThreadGroup中;
- (4) 線程如果啓動失敗了,線程組會對線程做失敗處理,將其從線程組中移除;
- (5) 如果一個線程生命週期結束了,再次調用start方法試圖啓動線程的時候,會提示IllegalThreadStateException異常,也就是處於TERMINATED狀態的線程沒辦法重新回到RUNNABLE或者RUNNING狀態;
示例驗證1:驗證線程是否可以啓動多次
Thread thread = new Thread(){
@Override
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(5);
} catch (Exception e) {
e.printStackTrace();
}
}
};
thread.start();
thread.start();
運行上述代碼,會出現以下異常信息:
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:705)
at com.wb.spring.test.TestThread.main(TestThread.java:40)
示例驗證2:線程生命週期結束,再次調用start方法啓動
Thread thread = new Thread(){
@Override
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(1);
System.out.println("線程運行完畢!");
} catch (Exception e) {
e.printStackTrace();
}
}
};
// 第一次啓動
thread.start();
try {
TimeUnit.MILLISECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
}
// 第二次啓動
thread.start();
運行以上示例代碼,將會得到如下的結果:
線程運行完畢!
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:705)
at com.wb.spring.test.TestThread.main(TestThread.java:48)
從結果中可以看出,線程運行完成之後,生命週期就結束了,不能再次啓動。
從上述示例中可以看到,都跑出了IllegalThreadStateException異常,但是兩次出現該異常的情況卻不一樣。第一種線程還活着,生命週期未結束,只是不允許多次重複啓動;而第二次是因爲線程的生命週期已經結束,試圖啓動已經結束的線程,就會拋出該異常。
2.Thread類中的設計模式-模板設計模式
簡介:模板設計模式指的是在繼承結構中,父類中來確定算法結構,而具體的算法實現細節交給子類來完成。比如Thread類中run方法的源碼如下,如果使用了Thread類的方式構造線程,其實run方法相當於一個空的實現,但是調用start方法的時候,卻能夠正確執行子類run方法的實現細節。
@Override
public void run() {
if (target != null) {
target.run();
}
}
這種設計模式有利於父類確定程序的結構。
模板設計模式示例:
public class TemplateTest {
public final void say(String msg) {
System.out.println("000000");
sayHello(msg);
System.out.println("000000");
}
protected void sayHello(String msg) {
}
public static void main(String[] args) {
TemplateTest test1 = new TemplateTest() {
@Override
protected void sayHello(String msg) {
System.out.println("***" + msg + "***");
}
};
test1.say("wb");
TemplateTest test2 = new TemplateTest() {
@Override
protected void sayHello(String msg) {
System.out.println("&&&" + msg + "&&&");
}
};
test2.say("bw");
}
}
輸入結果如下:
000000
***wb***
000000
000000
&&&bw&&&
000000
可以看到,需要輸入什麼符號,可以自由地由子類來控制!這就是模板設計模式帶來的好處!
以上就是Thread類啓動線程的過程及使用的設計模式詳解。歡迎評論轉發。
另提供一些免費的IT視頻資料,架構師,高可用,高併發等教程!如需要請查看https://www.592xuexi.com