重點是那個計算年銷售額的例子,認真看三遍。本文花了三個小時。java
GitHub歡迎star。git
小白認爲學習語言最好的方式就是模仿、思考別人爲何這麼寫。程序員
建立線程要麼是實現Runnable接口,要麼實現Thread類繼承。雖然這麼作很簡單,但建立出的線程會受到嚴重的限制--run方法不返回任何值給建立者。所以,許多程序員採用將返回值寫到文件中這樣的不完美解決方案。Runnable的另外一個問題在於不能拋出任何異常,所以必須在run方法中處理全部的異常。幸運的是,J2SE5提供了Callable和Future接口,用於解決此類的需求,Thread模擬了一類 沒有返回結果的任務的執行, 而Callable則是模擬 具備返回結果的任務的執行。 Future更進一步,模擬一類 可檢查進度並能夠取回結果的任務的執行github
CCallable接口相似於Runnable接口,擁有接受單個參數的call方法。數組
public interface Callable<V> { V call() throws Exception }
call方法能夠返回類型參數中指定的任意類型。須要注意的是,call方法和Runnable接口中的run方法並不相同,這裏的call方法會拋出被檢查的異常。使用Callable<void>可實現沒有返回值的Callable。框架
不能直接將Callable提交到Thread中執行,而是必須使用ExecutorService
來執行Callable對象。,能夠經過調用submit方法來完成這項任務。dom
<T> Future <T> submit (Callable <T> task)
submit方法返回一個Future對象異步
當調用方法將任務提交到Executor時,Executor會返回Future對象給調用方,Future接口的定義以下;函數
interface Future <V>
其中V表明的是Future中get方法返回值類型。調用方經過這個Future對象獲取所請求任務的控制權。get方法會將結果返回給調用方。若是計算沒有結束,get方法則會一直等下去,重載的get方法能夠接受時限參數,從而限制任務的最長執行時間。isDone方法檢測熱恩物是否執行結束,若是結束,就返回true。cancel方法會在任務正常結束以前嘗試取消,若是任務正常結束以前被取消,isCancelled會返回true學習
FutureTask是封裝類實現類Future與Runnable接口,並提供了一種便捷的方式,能夠將Callable轉爲Future與Runnable。
Caller是實現類Callable接口的Java類。Callable接口僅含有call方法,程序員在實現Callable接口時必須實現call方法。call方法一般會包含服務實現。要調用服務就必須實例化Caller類並調用call方法。若是但願上述過程異步完成,就須要採用某些特定機制調用call方法。Callbale接口和Runnable接口不一樣,後者能夠放在Thread類的構造函數中執行,Java提供了另外一個名爲ExecutorService
的類用於執行可調用任務。調用方首先建立或獲取ExecutorServie實例,並提交可調用任務以待執行。接下來框架會返回Future對象給調用方,這個Future隊形能夠用於檢查可調用任務的狀態並獲取執行結果。
重點知識在main函數中
/** * Created by guo on 2018/2/15. * 計算年銷售額 */ public class AnnualSalesCalc { private static int NUMBER_OF_CUSTOMERS = 100; //表示矩陣的行數 private static int NUMBER_OF_MONTHS = 12; //表示矩陣的列數 private static int salesMatrix[][]; //表明還沒有建立的二維整形數組。 /** * Summer用來計算每行的和。 * Summer類被聲明在AnnualSalesCalc的內部類,而且爲private和static。 * 之因此聲明爲private,是由於Summer類不會用在其餘地方。 * 聲明爲靜態是應爲Summer類會被靜態的main方法調用。Summer類還實現類Callable接口。 * 構造函數接受ID的整數值,並存儲在類變量中以備後用。 */ private static class Summer implements Callable { private int companyID; public Summer(int companyID) { this.companyID = companyID; } /** * Call方法被映射爲任何類型的泛型類,在這裏被映射爲Integer類型 * call方法經過for循環遍從來計算矩陣特定行內的全部元素的總和 */ public Integer call() throws Exception { int sum = 0; // for (int col = 0; col < NUMBER_OF_MONTHS; col++) { sum += salesMatrix[companyID][col]; } //在求和計算完成以後,程序會打印一條消息給用戶, // 代表這個Callable對象的任務已經結束,而且不管何時被詢問,均可以將計算結果返回給調用者。 System.out.printf("Totaling for client 1%02d completed%n", companyID); return sum; } } public static void main(String[] args) throws Exception { generateMatrix(); printMaxtrix(); //咱們須要執行者服務調用Callable對象,Executor類提供此服務 //newFixedThreadPool是Executors類的靜態方法,接受一個整型參數, //該參數的值決定了這個方法建立的線程數目。 //該方法會建立固定的線程池,用於執行不一樣的任務,而且在結束時會返回一個ExecutorService實例。 ExecutorService executor = Executors.newFixedThreadPool(10); //聲明的Set對象用於存儲Future對象,從而監控提交的任務。 Set<Future<Integer>> set = new HashSet<Future<Integer>>(); for (int row = 0; row < NUMBER_OF_CUSTOMERS; row++) { //爲矩陣的每一行都實例化類Summer對象,Summer對象同時也是Callable對象 Callable<Integer> callable = new Summer(row); //經過調用前面建立的executor對象的submit方法來運行這個Callable對象; //submit方法將Callable對象提交給線程池中的某一個線程,並返回一個Future對象給調用者。 //調用方能夠使用這個Future對象的get方法來獲取計算結果。 Future<Integer> future = executor.submit(callable); //咱們將返回的Future對象放在集合中,以便在最後階段獲取集合中全部元素之和,進而計算總和 set.add(future); } int sum = 0; //在提交 全部任務以後,使用for-each循環計算總和 for (Future<Integer> future : set) { sum += future.get(); } System.out.printf("%n The annual turnover (bags) : %s%n%n", sum); //最後關閉執行者服務,以釋放分配的全部資源。 executor.shutdown(); } }
爲了清晰,我把這兩個方法從類中拿出來了。
/** * 生成矩陣 */ private static void generateMatrix() { salesMatrix = new int[NUMBER_OF_CUSTOMERS][NUMBER_OF_MONTHS]; for (int i = 0; i < NUMBER_OF_CUSTOMERS; i++) { for (int j = 0; j < NUMBER_OF_MONTHS; j++) { //數組中的每一個元素都被初始化爲0-99之間的隨機數。 salesMatrix[i][j] = (int) (Math.random() * 100); } } } /** * 打印矩陣 */ private static void printMaxtrix() { System.out.print("\t\t"); String[] monthDisplayName = new DateFormatSymbols().getShortMonths(); for (String strName : monthDisplayName) { System.out.printf("%8s", strName); } System.out.printf("\n\n"); for (int i = 0; i < NUMBER_OF_CUSTOMERS; i++) { System.out.printf("Client ID : 1%02d", i); for (int j = 0; j < NUMBER_OF_MONTHS; j++) { System.out.printf("%8d", salesMatrix[i][j]); } System.out.println(); } System.out.println("\n\n"); }
輸出以下:
一月 二月 三月 四月 五月 六月 七月 八月 九月 十月 十一月 十二月 Client ID : 100 89 63 98 51 1 58 69 67 81 97 98 64 Client ID : 101 66 83 3 83 75 43 10 83 92 39 27 75 Client ID : 102 10 88 74 38 33 91 43 82 93 94 28 25 ...省略.. Client ID : 197 50 39 43 33 18 61 67 21 11 56 11 19 Client ID : 198 64 5 10 63 97 52 99 35 15 9 73 29 Client ID : 199 47 95 62 51 7 78 68 60 53 58 23 56 Totaling for client 101 completed Totaling for client 102 completed Totaling for client 110 completed ...省略.. Totaling for client 105 completed Totaling for client 109 completed The annual turnover (bags) : 60152