我有一系列任务(即Runnable
s)要由Executor
执行
每个任务都需要特定的条件才能继续。我很想知道是否有一种方法可以将Executor
配置为将任务移动到队列的末尾,并在条件有效且任务能够执行和完成时尝试稍后执行它们
所以行为类似于:
Thread-1
从队列中获取任务,并调用run
- 在
run
内部,条件尚未有效 - 任务停止,
Thread-1
将任务置于队列的末尾获取要执行的下一个任务 - 稍后
Thread-X
(来自线程池)从队列中再次拾取任务条件有效并且正在执行任务
在Java 6中,ThreadPoolExecutor
构造函数采用一个BlockingQueue<Runnable>
,用于存储排队的任务。您可以实现这样一个覆盖poll()
的阻塞队列,这样,如果尝试删除并执行"就绪"作业,则poll
将正常进行。否则,可运行文件将放在队列的后面,您可能会在短暂超时后再次尝试轮询。
除非您必须忙于等待,否则您可以向ScheduledExecutitorService添加一个具有适当轮询间隔的重复任务,在"有效"运行后取消或终止该轮询间隔。
ScheduleExecutorService ses = ...
ses.scheduleAtFixedRate(new Runnable() {
public void run() {
if (!isValid()) return;
preformTask();
throw new RuntimeException("Last run");
}
}, PERIOD, PERIOD, TimeUnit.MILLI_SECONDS);
首先创建执行器。
你有几种可能性。
如果我假设你的任务实现了一个简单的接口来查询它们的状态(类似于带有"NeedResschedule"或"Completed"的枚举),那么为你的任务实施一个包装器(实现Runnable
),它将任务和执行器作为通知参数。这个包装器将运行它绑定到的任务,然后检查它的状态,如果需要,在终止之前在执行器中重新安排它自己的副本。
或者,您可以使用exeception机制向包装器发出必须重新安排任务的信号。这个解决方案更简单,因为它不需要特定的接口来执行任务,所以可以在系统中顺利地抛出简单的Runnable
。但是,异常会导致更多的计算时间(对象构造、堆栈跟踪等)
以下是使用异常信号机制的包装器的可能实现。您需要实现扩展Throwable
的RescheduleException
类,该类可能由封装的可运行程序触发(在此设置中不需要为任务提供更具体的接口)。您也可以使用另一个答案中建议的简单RuntimeException
,但您必须测试消息字符串,以确定这是否是您正在等待的异常。
public class TaskWrapper implements Runnable {
private final ExecutorService executor;
private final Runnable task;
public TaskWrapper(ExecutorService e, Runnable t){
executor = e;
task = t;
}
@Override
public void run() {
try {
task.run();
}
catch (RescheduleException e) {
executor.execute(this);
}
}
下面是一个非常简单的应用程序,它随机启动200个已完成的任务,要求重新安排。
class Task implements Runnable {
@Override
public void run(){
if (Maths.random() > 0.5)
throw new RescheduleException();
}
}
public class Main {
public static void main(String[] args){
ExecutorService executor = Executors.newFixedThreadPool(10);
int i = 200;
while(i--)
executor.execute(new TaskWrapper(executor, new Task());
}
}
您还可以有一个专用线程来监视其他线程的结果(使用消息队列),并在必要时重新调度,但与其他解决方案相比,您会丢失一个线程。