6

我尝试使用 Java 的、FutureTaskFutureRunnable类型。CallableExecutorService

组成这些构建块的最佳实践是什么?

鉴于我有多个FutureTasks 并且我想按顺序执行它们。

当然,我可以创建另一个 FutureTask,它按顺序为每个子任务提交/等待结果,但我想避免阻塞调用。

另一种选择是让这些子任务在完成时调用回调,并在回调中安排下一个任务。但是走这条路,我如何创建一个适当的外部 FutureTask 对象,该对象还可以处理子任务中的异常而不产生那么多样板文件?

我在这里想念什么吗?

4

3 回答 3

4

非常重要的事情,虽然通常不会在教程中描述:

Runnables to be executed on an ExecutorService should not block. 这是因为每次阻塞都会关闭一个工作线程,如果 ExecutorService 的工作线程数量有限,就有陷入死锁(线程饥饿)的风险,如果 ExecutorService 的工作线程数量不受限制,那么就有风险内存不足。任务中的阻塞操作只会破坏 ExecutorService 的所有优点,因此只能在普通线程上使用阻塞操作。

FutureTask.get()是阻塞操作,所以可以在普通线程上使用,而不是来自 ExecutorService 任务。也就是说,它不能作为构建块,而只是将执行结果传递给主线程。

从任务构建执行的正确方法是在下一个任务的所有输入数据都准备好时启动下一个任务,这样任务就不必阻塞等待输入数据。因此,您需要一种门来存储中间结果并在所有参数到达时启动新任务。因此,任务不会显式地启动其他任务。因此,由用于参数的输入套接字和用于计算它们的 Runnable 组成的门可以被视为 ExcutorServices 上计算的正确构建块。

这种方法称为数据流或工作流(如果不能动态创建门)。

像 Akka 这样的 Actor 框架使用这种方法,但受限于 Actor 是具有单个输入套接字的门这一事实。

我编写了一个真实的数据流库,发布在https://github.com/rfqu/df4j

于 2013-02-05T12:36:12.913 回答
0

我尝试对 ScheduledFuture 做类似的事情,试图在向用户显示内容之前造成延迟。这就是我想出的,只需对所有“延迟”使用相同的 ScheduledFuture。代码是:

    public static final ScheduledExecutorService scheduler = Executors
        .newScheduledThreadPool(1);
    public ScheduledFuture delay = null;

    delay = scheduler.schedule(new Runnable() {
    @Override
    public void run() {
    //do something
    }
    }, 1000, TimeUnit.MILLISECONDS);

    delay = scheduler.schedule(new Runnable() {
    @Override
    public void run() {
    //do something else
    }
    }, 2000, TimeUnit.MILLISECONDS);

希望这可以帮助安迪

于 2013-02-05T10:14:23.360 回答
0

通常的做法是:

  • 决定 ExecutorService(哪种类型,多少个线程)。
  • 决定任务队列(它可以是非阻塞的多长时间)。

如果您有一些外部代码等待任务结果: * 将任务作为 Callables 提交(只要您没有用完队列,这就是非阻塞的)。* 呼叫获得未来。

如果您希望在任务完成后自动执行某些操作:

  • 您可以作为 Callables 或 Runnables 提交。
  • 只需添加您需要在最后执行的任务作为任务中的最后一个代码。使用 Activity.runOnUIThread 这些最终动作需要修改 GUI。

通常,您不应该主动检查何时可以提交更多任务或安排回调以提交它们。线程队列(阻塞,如果愿意)将为您处理这个问题。

于 2013-02-05T10:16:59.023 回答