java并发编程-Executor框架 + Callable + Future

from: https://www.cnblogs.com/shipengzhi/articles/2067154.html

import java.util.concurrent.*;
public class ConcurrentCalculator2 { //from: https://www.cnblogs.com/shipengzhi/articles/2067154.html
    private ExecutorService executorService;
    private CompletionService<Long> completionService;
    private int cpuCoreNumber;

    public ConcurrentCalculator2() {
        cpuCoreNumber = Runtime.getRuntime().availableProcessors();
        executorService = Executors.newFixedThreadPool(cpuCoreNumber);
        completionService = new ExecutorCompletionService<Long>(executorService);
    }

    public Long sum(final int[] numbers) {
        for (int i = 0; i < cpuCoreNumber; i++) {               //(页数)根据CPU核心个数拆分任务,创建FutureTask并提交到Executor
            int increment = numbers.length / cpuCoreNumber + 1; //(每页多少条)
            int start = increment * i;                          //首条数据-->当前页    的
            int end = increment * i + increment;                //首条数据-->(当前页+1)的
            if (end > numbers.length)
                end = numbers.length;
            SumCalculator subCalc = new SumCalculator(numbers, start, end, (i+1));
            if (!executorService.isShutdown()) {
                completionService.submit(subCalc);
            }
        }
        System.out.println("所有计算任务提交完毕, 主线程接着干其他事情!");
        return getResult();
    }

    public Long getResult() { //迭代每个只任务,获得部分和,相加返回
        Long result = 0l;
        int index =0;
        System.out.println("-->2-"+ Thread.currentThread().getName()+"线程 List<Future<Long>>集合中的第一个元素开始遍历之前【就开始 全部执行掉MyCallable构造方法(不一定按集合内部顺序)!】");
        for (int i = 0; i < cpuCoreNumber; i++) {
            try {
                index++;
                Long subSum = completionService.take().get();
                System.out.println("-->4-"+Thread.currentThread().getName()+"线程 "+"-List<Future<Long>>集合遍历ing(一定按集合内部顺序)【有时候3和4会<成对>交叉执行】-- 打印第:"+index+"页累计的和:" + subSum);
                result += subSum;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        System.out.println("-->5-总结-->线程的实现方法总是先一步于List<Future<Long>>的遍历(线程的impl分页计算各页的总和,List<Future>负责把所有页的和累加!");
        return result;

    }

    public void close() {
        executorService.shutdown();
    }

    class SumCalculator implements Callable<Long> {//Runnable
        private int[] numbers;
        private int start;
        private int end;
        private int index;

        public SumCalculator(final int[] numbers, int start, int end, int index) {
            this.numbers = numbers;
            this.start = start;
            this.end = end;
            this.index = index;
            System.out.println("-->1-"+Thread.currentThread().getName() +"线程-生成子线程计算任务(调用构造函数)第 "+ index +"次");
        }

        @Override
        public Long call() throws Exception {
            Long sum = 0l;
            for (int i = start; i < end; i++) {
                sum += numbers[i];
            }
            //子线程-->休眠5秒钟,观察主线程行为,预期的结果是主线程会继续执行,到要取得FutureTask的结果是等待直至完成。
            Thread.sleep(3000);

            System.out.println("-->3-" + Thread.currentThread().getName() + "线程执行计算任务 打印current数据:" +"startIndex:"+ start + ";endIndex:" + end+ ";sum:" + "=" + sum);
            return sum;
        }
    }
    public static void main(String[] args) {
        int[] numbers = new int[] { 0, 1, 2  };
        ConcurrentCalculator2 calculator = new ConcurrentCalculator2();
        Long sum = calculator.sum(numbers);
        System.out.println("-->10-res:"+sum);
        calculator.close();
    }


    /**

     * Callable 和 Future接口

     * Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。

     * Callable和Runnable有几点不同:

     * (1)Callable规定的方法是call(),而Runnable规定的方法是run().

     * (2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。

     * (3)call()方法可抛出异常,而run()方法是不能抛出异常的。

     * (4)运行Callable任务可拿到一个Future对象,

     * Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。

     * 通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。

     */
}