讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 第9章 多線程和並發 >

第9章 多線程和並發

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方法。