【问题标题】:How to stop a Callable thread process after a exception in one thread?如何在一个线程中发生异常后停止可调用线程进程?
【发布时间】:2022-01-04 10:31:04
【问题描述】:

我正在使用 Callable 和 ExecutorService 在我的应用程序中使用多线程。

如果一个线程抛出任何异常需要停止所有线程,即使它的工作已经完成并且需要将该异常抛出给调用类方法。为此,我使用了一个 shutDownNow() 方法。

这是正确的方法吗?或者还有其他有效的方法吗?

        
ExecutorService exSvc = Executors.newFixedThreadPool(5);
exSvc.setKeepAliveTime(60, TimeUnit.SECONDS);

List<Future<Integer>> futureList = new LinkedList();
for(int i=0; i<50;i++){
    futureList.add(exSvc.submit(
        new Callable<Integer>() {
            public Integer call()  throws Exception{
                int num =  new Random().nextInt(1000);
                if(num==500){
                    throw new Exception("Error");
                }
            return num;
            }
        }));
}

for(int i=0; i<50; i++){
 
   try {
    int value =  futureList.get(i).get();
   } catch (Exception e) {
        exSvc.shutdownNow();
        throw new Exception("Error");
   } 
}


【问题讨论】:

    标签: java multithreading exception concurrency


    【解决方案1】:

    正如ciamei所说,如果你想尽早检测到异常,也许你可以使用CountDownLatch

            CountDownLatch cdl = new CountDownLatch(1);
            AtomicInteger successCount = new AtomicInteger();
            AtomicBoolean fail = new AtomicBoolean();
            int taskSize = 50;
            for(int i=0; i<taskSize;i++){
                futureList.add(exSvc.submit(new Callable<Integer>() {
                    @Override
                    public Integer call() throws Exception {
                        int num =  new Random().nextInt(1000);
                        if(num == 500){
                            fail.set(true);
                            cdl.countDown();
                            throw new Exception("Error");
                        }
                        if (successCount.incrementAndGet() == taskSize) {
                            cdl.countDown();
                        }
                        return num;
                    }
                }));
            }
            try {
                cdl.await();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            if (fail.get()) {
                exSvc.shutdownNow();
                throw new Exception("Error");
            }
            //...
    

    【讨论】:

    • 感谢新方法,何时需要调用 future.get()?是在 cdl.await() 之前还是之后?
    • cdl.await();之后,如果fail.get()为false,则表示所有任务执行成功,然后调用future#get获取所有结果。
    • 这可以正常工作,但会引入一些同步损失,因为现在所有任务都必须以原子方式递增相同的计数器。如果任务相当长,则惩罚相对而言可以忽略不计。另一方面,如果任务很短,这可能会增加一些明显的开销。
    【解决方案2】:

    这应该适用于您的示例。但是,请务必正确处理可调用对象中的线程中断。

    请注意,只有在您注意到其中一项任务引发异常之后,才会尽最大努力取消其他任务。例如,如果任务没有。 5 已经抛出异常,你必须等待任务 0-4 完成,然后你才注意到任务 5 失败了。

    或者,您可以像here 这样子类化 ThreadPoolExecutor,以尽早检测到异常。

    【讨论】:

    • As per this 当我需要调用 afterExecute 方法时。我是否需要将该类用于 ExecutorService 本身或仅用于获得未来。
    • @DAK 不,在替代方法中,您不创建 newFixedThreadPool,而是创建您自己的 ThreadExecutor 实现。 afterExecute 方法将被 Executor 的其他一些方法自动调用。你只需重新实现那一点。
    • 谢谢 试试这个。 afterExecute 方法接受 Runnable 对象,但我使用的是可调用对象,有什么需要更改的吗?
    • @DAK TBH 我没有尝试过,文档对此有点模糊。一方面,您有提交(可运行)和提交(可调用),但正如您所说的 afterExecute 只接受可运行。我猜想内部 Callable 被放在了一些实现 Runnable 和 Future 的对象中,代码应该按原样工作。
    • @DAK 如果您尝试过并且有效,请回信,以便我们确认这是正确的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-10
    • 2022-07-19
    • 2013-05-22
    • 1970-01-01
    • 1970-01-01
    • 2018-08-27
    • 2018-06-23
    相关资源
    最近更新 更多