现在关闭ExecutionException

时间:2016-10-27 16:08:04

标签: java java.util.concurrent

我阅读了很多关于ExecutorService的帖子,但我找不到做我需要的方法。

我需要一些并发线程。当其中任何一个抛出自定义异常时,所有剩余的任务都将被取消。

这是我所做的一个例子。该任务同时工作,但在异常时不会中断。

public class Main {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        List<Future> futures = new ArrayList<Future>();

        futures.add(executorService.submit(new Callable<Void>() {
            public Void call() throws Exception {
                Thread.sleep(5000);
                System.out.println("Task 1 done");
                return null;
            }
        }));

        futures.add(executorService.submit(new Callable<Void>() {
            public Void call() throws Exception {
                Thread.sleep(2000);
                System.out.println("Task 2 done");
                if (true) {
                    throw new CustomException("Error on task 2");
                }
                return null;
            }
        }));
        executorService.shutdown();

        try {
            executeFutures(futures);
        } catch (CustomException ex) {
            System.out.println("Received:" + ex.getMessage());
            executorService.shutdownNow();
        }    
    }

    private static void executeFutures(List<Future> futures) throws CustomException {
        try {
            for (Future f : futures) {
                f.get();
            }
        } catch (ExecutionException | InterruptedException e) {
            if (e.getCause() instanceof CustomException) {
                throw (CustomException) e.getCause();
            }
        }
    }    
}

这是输出:

Task 2 done  //exception is thrown here but task1 continue.
Task 1 done
Received:Error on task 2

任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

您的问题是由于方法executeFutures在与长任务相对应的第一个f.get()实例上进行主线程调用Future,这使得它等待持续时间无论发生什么,任务至少要等5秒。完成后,它会在已经结束的第二个f.get()上调用Future,以便立即从CustomException获取ExecutionException并调用executorService.shutdownNow(),但它是已经太晚了,因为没有更多的任务可以打断。

你可以做的是使用类型Callable的装饰器,它会在抛出CustomException时自动关闭线程池,这样线程池将被线程直接关闭已经执行了抛出异常而不是使用主线程的任务。

这样的事情:

public class AutoShutdown<V> implements Callable<V> {

    private final ExecutorService executorService;
    private final Callable<V> task;

    public AutoShutdown(final ExecutorService executorService, final Callable<V> task) {
        this.executorService = executorService;
        this.task = task;
    }

    @Override
    public V call() throws Exception {
        try {
            return task.call();
        } catch (CustomException e) {
            executorService.shutdownNow();
            throw e;
        }
    }
}

然后你将需要通过装饰器提交你的任务,如下:

futures.add(
    executorService.submit(
        new AutoShutdown<>(
            executorService,
            new Callable<Void>() {
                public Void call() throws Exception {
                    Thread.sleep(5000);
                    System.out.println("Task 1 done");
                    return null;
                }
            }
        )
    )
);

futures.add(
    executorService.submit(
        new AutoShutdown<>(
            executorService,
            new Callable<Void>() {
                public Void call() throws Exception {
                    Thread.sleep(2000);
                    System.out.println("Task 2 done");
                    if (true) {
                        throw new CustomException("Error on task 2");
                    }
                    return null;
                }
            }
        )
    )
);

<强>输出:

Task 2 done

正如您在输出中看到的那样,任务1很快就被打断了。

  

没有抛出消息“Received:Error on task 2”,所以看起来   就像成功执行一样,并非如此

不,这只是因为第一次调用f.get()会按预期抛出InterruptedException,这使得它从executeFutures退出,因为捕获是在循环外执行的,将其移动到循环内如下:

private static void executeFutures(List<Future> futures) throws CustomException {
    for (Future f : futures) {
        try {
            f.get();
        } catch (ExecutionException | InterruptedException e) {
            if (e.getCause() instanceof CustomException) {
                throw (CustomException) e.getCause();
            }
        }
    }
}

<强>输出:

Task 2 done
Received:Error on task 2