多線程應用有兩種實現方式,一種是實現Runnable接口,另一種是繼承Thread類,這兩個方式都有缺點:run方法沒有返回值,不能拋出異常(這兩個缺點歸根到底是Runable接口的缺陷,Thread也是實現了Runnable接口),如果需要知道一個線程的運行結果就需要用戶自行設計,線程類自身也不能提供返回值和異常。但是從Java 1.5開始引入了一個新的接口Callable,它類似於Runable接口,實現它就可以實現多線程任務,Callable的接口定義如下:
public interface Callable<V>{
//具有返回值,並可拋出異常
V call()throws Exception;
}
實現Callable接口的類,只是表明它是一個可調用的任務,並不表示它具有多線程運算能力,還是需要執行器來執行的。我們先編寫一個任務類,代碼如下:
//稅款計算器
class TaxCalculator implements Callable<Integer>{
//本金
private int seedMoney;
//接收主線程提供的參數
public TaxCalculator(int_seedMoney){
seedMoney=_seedMoney;
}
@Override
public Integer call()throws Exception{
//複雜計算,運行一次需要10秒
TimeUnit.MILLISECONDS.sleep(10000);
return seedMoney/10;
}
}
這裡模擬了一個複雜運算:稅款計算器,該運算可能要花費10秒鐘的時間,此時不能讓用戶一直等著吧,需要給用戶輸出點什麼,讓用戶知道系統還在運行,這也是系統友好性的體現:用戶輸入即有輸出,若耗時較長,則顯示運算進度。如果我們直接計算,就只有一個main線程,是不可能有友好提示的,如果稅金不計算完畢,也不會執行後續動作,所以此時最好的辦法就是重啟一個線程來運算,讓main線程做進度提示,代碼如下:
public static void main(Stringargs)throws Exception{
//生成一個單線程的異步執行器
ExecutorService es=Executors.newSingleThreadExecutor();
//線程執行後的期望值
Future<Integer>future=es.submit(new TaxCalculator(100));
while(!future.isDone()){
//還沒有運算完成,等待200毫秒
TimeUnit.MILLISECONDS.sleep(200);
//輸出進度符號
System.out.print("#");
}
System.out.println("\n計算完成,稅金是:"+future.get()+"元");
es.shutdown();
}
在該段代碼中,Executors是一個靜態工具類,提供了異步執行器的創建能力,如單線程執行器newSingleThreadExecutor、固定線程數量的執行器newFixedThreadPool等,一般它是異步計算的入口類。Future關注的是線程執行後的結果,比如有沒有運行完畢,執行結果是多少等。此段代碼的運行結果如下所示:
##################################################
計算完成,稅金是:10元
執行時,「#」會依次遞增,表示系統正在運算,為用戶提供了運算進度。此類異步計算的好處是:
盡可能多地佔用系統資源,提供快速運算。
可以監控線程執行的情況,比如是否執行完畢、是否有返回值、是否有異常等。
可以為用戶提供更好的支持,比如例子中的運算進度等。