大家在开心的过年,而我在苦逼的学习(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
相关文章
相关标签/搜索