我尝试使用Java的FutureTask
,Future
,Runnable
,Callable
和ExecutorService
类型。
构建这些构建块的最佳做法是什么?
鉴于我有多个FutureTask
并且我想按顺序执行它们。
当然,我可以创建另一个FutureTask,它按顺序提交/等待每个子任务的结果,但我想避免阻塞调用。
另一种选择是让这些子任务在完成后调用回调,并在回调中安排下一个任务。但是走这条路,如何创建一个合适的外部FutureTask对象,它也处理子任务中的异常而不会产生那么多的样板?
我在这里想念一下吗?
答案 0 :(得分: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。
答案 1 :(得分: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);
希望这会有所帮助 安迪
答案 2 :(得分:0)
通常的做法是:
如果您有一些等待任务结果的外部代码: *以Callables的形式提交任务(只要您没有用完队列,这就是非阻塞)。 *打电话给未来。
如果您希望在任务完成后自动执行某些操作:
通常情况下,您不应主动检查何时可以再提交一项任务或安排回调,以便提交它们。线程队列(阻塞,如果愿意)将为您处理。