【并发编程】- ExecutorService的invokeAll方法)快的任务执行异常,慢的任务执行正确情况

344 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

方法invokeAll(Collection tasks)快的任务执行异常,慢的任务执行正确

第一个线程执行代码如下:

public class OFirstCallable implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println(super.getClass().getName()+ "线程执行开始时间:"+ System.currentTimeMillis());
        for (int i = 0; i <  123456; i++) {
            Math.random();
            Math.random();
            Math.random();
            System.out.println(super.getClass().getName()+(i+1));
        }
        System.out.println(super.getClass().getName()+"线程执行结束时间:" + System.currentTimeMillis());
        if(true){
            System.out.println(Thread.currentThread().getName());
            throw new Exception(super.getClass().getName()+"出现异常了");
        }
        return super.getClass().getName();
    }
}

第二个线程执行代码如下:

public class OSecondCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            System.out.println(super.getClass().getName()+ "线程执行开始时间:"+ System.currentTimeMillis());
            for (int i = 0; i <  123456; i++) {
                Math.random();
                Math.random();
                Math.random();
                System.out.println(super.getClass().getName()+(i+1));
            }
            System.out.println(super.getClass().getName()+"线程执行结束时间:" + System.currentTimeMillis());
            return super.getClass().getName();
        }
}

运行类执行代码如下:

public class invokeAllRun {
    public static void main(String[] args) {
        OFirstCallable oFirstCallable = new OFirstCallable();
        OSecondCallable oSecondCallable = new OSecondCallable();
        List<Callable<String>> list = new ArrayList<>();
        list.add(oFirstCallable);
        list.add(oSecondCallable);
        ExecutorService executorService = Executors.newCachedThreadPool();
        System.out.println("invokeAll方法执行开始时间:" + System.currentTimeMillis());
        try {
            List<Future<String>> futureList = executorService.invokeAll(list);
            System.out.println("invokeAll方法执行结束时间: " + System.currentTimeMillis());
            for (int i= 0;i< futureList.size();i++) {
                System.out.println("返回的结果:" + futureList.get(i).get() + " 返回结果时间: "+ System.currentTimeMillis());
            }
            System.out.println("主线程结束!");
        } catch (InterruptedException e) {
            System.out.println("进入 InterruptedException 异常捕获");
            e.printStackTrace();
        } catch (ExecutionException e) {
            System.out.println("进入 ExecutionException 异常捕获");
            e.printStackTrace();
        }
    }
}

运行结果如下:

com.ozx.concurrentprogram.executor.service.october.OFirstCallable 123455

com.ozx.concurrentprogram.executor.service.october.OFirstCallable 123456

com.ozx.concurrentprogram.executor.service.october.OFirstCallable线程执行结束时间:1664621162300

pool-1-thread-1 invokeAll方法执行结束时间: 1664621162300

进入 ExecutionException 异常捕获 java.util.concurrent.ExecutionException: java.lang.Exception: com.ozx.concurrentprogram.executor.service.october.OFirstCallable出现异常了 at java.util.concurrent.FutureTask.report(FutureTask.java:122) at java.util.concurrent.FutureTask.get(FutureTask.java:192) at com.ozx.concurrentprogram.executor.controller.october.invokeAllRun.main(invokeAllRun.java:28) Caused by: java.lang.Exception: com.ozx.concurrentprogram.executor.service.october.OFirstCallable出现异常了 at com.ozx.concurrentprogram.executor.service.october.OFirstCallable.call(OFirstCallable.java:25) at com.ozx.concurrentprogram.executor.service.october.OFirstCallable.call(OFirstCallable.java:10) at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266) at java.util.concurrent.FutureTask.run(FutureTask.java) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)

从运行结果看出在list添加Callable的顺序与运行结果也有关系,在上面先添加第一个线程的任务,再添加第二个线程的任务,所有在执行for循环中第一次循环时取得是第一个线程的Future对象,由于第一个线程执行速度快并且出现了异常,则在第一次for时出现ExecutionException异常,不再继续执行第2次循环,进入主线程方法中的catch捕获语句块。因为第一个线程出现了异常,在第一次for循环取得返回值时产生异常并退出for循环,就导致了没有执行第二次for循环,所以在主线程方法中没有获得第二个线程的返回值,程序流程直接进入catch捕获语句块,导致主线程字符串也没有输出。