We're here to put a dent in the universe.Otherwise why else even be here?
活著就是為了改變世界,難道還有其他原因嗎?
——Steve Paul Jobs(史蒂夫·喬布斯)
多線程技術可以更好地利用系統資源,減少對用戶的響應時間,提高系統的性能和效率,但同時也增加了系統的複雜性和運維難度,特別是在高並發、大壓力、高可靠性的項目中,線程資源的同步、搶佔、互斥等都需要慎重考慮,以避免產生性能損耗和線程死鎖。
建議118:不推薦覆寫start方法
多線程比較簡單的實現方式是繼承Thread類,然後覆寫run方法,在客戶端程序中通過調用對象的start方法即可啟動一個線程,這是多線程程序的標準寫法。不知道讀者是否還能回想起自己的第一個多線程demo呢?估計一般是這樣寫的:
class MultiThread extends Thread{
@Override
public void start(){
//調用線程體
run();
}
@Override
public void run(){
//MultiThread do something.
}
}
覆寫run方法,這好辦,寫上自己的業務邏輯即可,但為什麼要覆寫start方法呢?最常見的理由是:要在客戶端中調用start方法啟動線程,不覆寫start方法怎麼啟動run方法呢?於是乎就覆寫了start方法,在方法內調用run方法。客戶端代碼是一個標準程序,代碼如下:
public static void main(Stringargs){
//多線程對像
MultiThread multiThread=new MultiThread();
//啟動多線程
multiThread.start();
}
相信讀者都能看出這是一個錯誤的多線程應用,main方法根本就沒有啟動一個子線程,整個應用程序中只有一個主線程在運行,並不會創建任何其他的線程。對此,有很簡單的解決辦法,只要刪除MultiThread類中的start方法即可。
然後呢?就結束了嗎?是的,很多時候確實到此結束了。找我解惑的同事或朋友中,很少有人會問為什麼不必而且不能覆寫start方法,僅僅就是因為「多線程應用就是這樣寫的」這個原因嗎?
要說明這個問題,就需要看一下Thread類的源代碼了。Thread類的start方法的代碼如下。
public synchronized void start(){
//判斷線程狀態,必須是未啟動狀態
if(threadStatus!=0)
throw new IllegalThreadStateException();
//加入線程組中
group.add(this);
//分配棧內存,啟動線程,運行run方法
start0();
//在啟動前設置了停止狀態
if(stopBeforeStart){
stop0(throwableFromStop);
}
}
//本地方法
private native void start0();
這裡的關鍵是本地方法start0,它實現了啟動線程、申請棧內存、運行run方法、修改線程狀態等職責,線程管理和棧內存管理都是由JVM負責的,如果覆蓋了start方法,也就是撤消了線程管理和棧內存管理的能力,這樣如何啟動一個線程呢?事實上,不需要關注線程和棧內存的管理,只需要編碼者實現多線程的邏輯即可(即run方法體),這也是JVM比較聰明的地方,簡化多線程應用。
那可能有讀者要問了:如果確實有必要覆寫start方法,那該如何處理呢?這確實是一個罕見的要求,不過,要覆寫也很容易,只要在start方法中加上super.start即可,代碼如下:
class MultiThread extends Thread{
@Override
public void start(){
/*線程啟動前的業務處理*/
super.start();
/*線程啟動後的業務處理*/
}
@Override
public void run(){
//MultiThread do something.
}
}
注意看start方法,調用了父類的start方法,沒有主動調用run方法,這是由JVM自行調用的,不用我們顯式實現,而且是一定不能實現。此方式雖然解決了「覆寫start方法」的問題,但是基本上無用武之地,到目前為止還沒有發現一定要覆寫start方法的多線程應用,所有要求覆寫start的場景,都可以用其他的方式來實現,例如類變量、事件機制、監聽等方式。
注意 繼承自Thread類的多線程類不必覆寫start方法。