【问题标题】:How do I know when all threads in a ExecutorService are finished?我如何知道 ExecutorService 中的所有线程何时完成?
【发布时间】:2015-04-04 20:04:19
【问题描述】:

我知道shutdown()awaitTermination() 存在。问题是池中的可运行对象需要能够添加未知数量的其他可运行对象(不能使用倒计时),如果我调用shutdown(),这些任务将被拒绝。我怎么知道它们什么时候完成?

【问题讨论】:

  • 当一个任务 (Runnable) T 产生另外 n 个任务时,当所有这 n 个任务都完成时,你会说这个任务 T 完成了吗?那么任务 T 与其子任务之间是否存在(概念上的)依赖关系?
  • 对。我忽略了我正在使用newFixedThreadPool,其中运行线程的最大数量是处理器的数量。一旦一个runnable产生了其他的,它就没有更多的工作要做了。如果它一直等到它生成的可运行对象完成,它就会坐在那里什么都不做,并且由于线程数的限制,程序永远不会结束。
  • 因此您可能应该使用ForkJoinTask

标签: java multithreading runnable executorservice


【解决方案1】:

使用Future 而不是Runnable。有这个Future#isDone 方法可以帮助你。

如果您从Callable 返回没有任何意义,请使用Callable<Void>Future<Void>

【讨论】:

    【解决方案2】:

    与其将Runnable 任务提交给Executor,不如使用ForkJoinTask/ForkJoinPoolForkJoinTaskForkJoinPool 内部运行,可以生成任意数量的(子)任务并等待它们完成,而不会真正阻塞当前线程。 ForkJoinTask 在其所有子任务完成后即完成,因此整个计算在初始(根)ForkJoinTask 完成时完成。

    详情请见Oracle - The Java™ Tutorials - Fork/Join

    由于你所有的任务都没有结果(Runnable),你应该继承RecursiveAction(它本身就是ForkJoinTask的子类)。实现方法compute(),并通过调用invoke(subtask)invokeAll(subtask1, subtask2, ...)subtask.fork() 后跟subtask.join() 生成任意数量的新任务。

    整个计算执行如下:

    MyRecursiveAction task = new MyRecursiveAction(params);
    ForkJoinPool pool = new ForkJoinPool(numberOfThreads);
    pool.invoke(task); // will block until task is done
    

    不幸的是,Fork/Join 的优点有一些限制,例如:

    (...) 理想情况下,计算应避免同步方法或块,并且 除了加入之外,应该尽量减少其他阻塞同步 其他任务或使用同步器,例如广告中的 Phaser 配合fork/join调度。可细分的任务也应该 不执行阻塞 I/O,理想情况下应该访问 完全独立于其他正在运行的任务访问的那些。这些 通过不允许检查异常来松散地执行指南 例如要抛出的 IOExceptions。 (...)

    更多详情请见API docs of ForkJoinTask

    【讨论】:

      【解决方案3】:

      如果您可以使用Guava Futures,您可以使用Futures.allAsListFutures.successfulAsList。这允许您将从ExecutorService 返回的多个Future 实例包装到单个Future 中,然后您可以使用isDone()(或仅get(),例如没关系,如果你想阻止直到完成)。

      【讨论】:

        猜你喜欢
        • 2015-02-14
        • 1970-01-01
        • 2018-06-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多