大家在開心的過年,而我在苦逼的學習(Callable和Future接口)。這就是人生,少壯不努力,老大徒傷悲!!!

Callabl、Future、Executors與分支/合併框架

重點是那個計算年銷售額的例子,認真看三遍。本文花了三個小時。java

GitHub歡迎star。git

小白認爲學習語言最好的方式就是模仿思考別人爲何這麼寫。程序員

19.1 Callable和Future接口

建立線程要麼是實現Runnable接口,要麼實現Thread類繼承。雖然這麼作很簡單,但建立出的線程會受到嚴重的限制--run方法不返回任何值給建立者。所以,許多程序員採用將返回值寫到文件中這樣的不完美解決方案。Runnable的另外一個問題在於不能拋出任何異常,所以必須在run方法中處理全部的異常。幸運的是,J2SE5提供了Callable和Future接口,用於解決此類的需求,Thread模擬了一類 沒有返回結果的任務的執行, 而Callable則是模擬 具備返回結果的任務的執行。 Future更進一步,模擬一類 可檢查進度並能夠取回結果的任務的執行github

19.1.1 Callable接口

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對象異步

19.1.2 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
相關文章
相關標籤/搜索