2012-11-20 131 views
5

我有一系列的任务(即Runnable s)由Executor执行。
每个任务都需要一定的条件才能继续。我想知道是否有办法以某种方式配置Executor将任务移动到队列末尾,并在条件有效并且任务能够执行并完成时稍后尝试执行它们。
所以行为是这样的:从队列有没有办法将任务放回执行者队列

  1. Thread-1取任务,run
  2. run条件尚未生效
  3. 任务停止和Thread-1地任务在队列的末尾和 获取下一个任务执行
  4. 稍后Thread-X(来自线程池)从队列中再次选择任务条件有效 并且任务正在执行
+0

只需创建一个等于当前值的新任务并将其放入队列。完成当前任务 – hoaz

+0

@hoaz:您的意思是从任务内部引用执行程序? – Cratylus

+1

@Cratylus - 是的,如果任务有一个引用回执行者,它可以重新排队。只要确保执行程序的待处理任务队列是无限的,或者您可以最终锁定自己。 – jtahlborn

回答

2

首先创建执行程序。

你有几种可能性。

如果我想,你的任务实现一个简单的界面来查询(像“NeedReschedule”或“已完成”的枚举的东西)自己的状态,然后实现(实现Runnable)为您的任务的包装,其将采取的任务,执行者作为instanciation参数。这个包装器将运行它所绑定的任务,之后检查它的状态,并且如果需要的话在终止之前在执行器中重新调度它自己的一个副本。

或者,您可以使用执行机制来指示该任务必须重新计划的包装器。 这个解决方案更简单一些,因为它不需要特定的接口来完成任务,所以简单的Runnable可以毫无困难地投入到系统中。但是,异常会产生更多计算时间(对象构建,堆栈跟踪等)。

以下是使用异常信号机制的包装器的可能实现。 您需要实现RescheduleException类扩展Throwable,这可能会被封装的可运行模块触发(无需为此设置中的任务提供更具体的接口)。您也可以使用其他答案中建议的简单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()); 
} 
} 

你也可以有一个专门的线程来监视其他线程的结果(使用消息队列),如果需要重新安排,但你失去了一个线程,相比于其他的解决方案。

+0

您能否提供一些关于您建议的更多细节? – Cratylus

+0

'这个包装器将运行它所绑定的任务,然后检查它的状态。怎么使用'Callable's? – Cratylus

+0

提供了一个简单的实现(代码未经测试)。 – didierc

3

在Java 6中,ThreadPoolExecutor构造函数采用BlockingQueue<Runnable>,该构造函数用于存储排队的任务。您可以实施这种覆盖poll()的阻止队列,以便如果尝试删除并执行“就绪”作业,则poll正常进行。否则,可运行程序位于队列的后面,并且您可能会在短暂超时后尝试再次轮询。

+0

这难道不是依赖于实现细节?我怎么知道确保实现将使用*民调*。我不希望它是基于'Oracle'源代码,例如 – Cratylus

+0

我不知道这是否确实使用“民意调查”。我知道它会使用'remove()','poll()',take()或poll(time,unit)''之一,因为这些是根据文档从阻塞队列中移除的方法。 – mbatchkarov

+0

除此之外,虽然你的建议是有吸引力(+1),我看不出它如何帮助更换预定条件下,检查是不是一些全球性的条件,它是'run'在'Runnable'的状态的一部分 – Cratylus

3

除非你得有忙等待,你可以重复任务添加到ScheduledExecutorService的与您取消或杀死后,它是“有效的”运行适当的轮询间隔。

ScheduleExecutorService ses = ... 

ses.scheduleAtFixedRate(new Runnable() { 
    public void run() { 
     if (!isValid()) return; 
     preformTask(); 
     throw new RuntimeException("Last run"); 
    } 
}, PERIOD, PERIOD, TimeUnit.MILLI_SECONDS); 
+0

什么是'RuntimeException'呢? – Cratylus

+0

它是停止任务重复的最简单方法。有其他选择,但他们至少是丑陋和更复杂。 –

+0

该异常不会影响队列中的下一个任务,对吧? – Cratylus

相关问题