java:组合多线程/单线程任务队列

时间:2010-01-28 19:04:43

标签: java multithreading concurrency

我喜欢ExecutorService系列的类/接口。我不必担心线程;我接受一个ExecutorService实例并使用它来安排任务,如果我想使用8线程或16线程池,那么,很好,我根本不用担心,它只是发生取决于ExecutorService的设置方式。乌拉!

但如果我的某些任务需要按顺序执行,我该怎么办?理想情况下,我会要求ExecutorService让我在一个线程上安排这些任务,但似乎没有任何方法可以这样做。

编辑:任务未提前知道,它们是由各种事件不规律地生成的无限系列任务(想想随机/未知到达过程:例如盖革的点击计数器或击键事件)。

4 个答案:

答案 0 :(得分:6)

您可以编写Runnable的实现来执行某些任务并以串行方式执行它们。

类似的东西:

public class SerialRunner implements Runnable {
    private List<Runnable> tasks;

    public SerialRunner(List<Runnable> tasks) {
        this.tasks = tasks;
    }

    public void run() {
        for (Runnable task: tasks) {
            task.run();
        }
    }
}

答案 1 :(得分:2)

我正在使用使用Executors.newSingleThreadExecutor()创建的单独执行程序来执行我想要排队的任务,并且一次只运行一个。 另一种方法是只编写几个任务并提交一个,

executor.submit(new Runnable() {
   public void run() {
        myTask1.call();
        myTask2.call();
        myTask3.call();
    }});

如果myTask2引发异常,如果仍希望myTask1运行,则可能需要更详细。

答案 2 :(得分:1)

我这样做的方法是通过一些自行开发的代码,根据任务所说的密钥将工作流转换到不同的线程(这可以是完全任意的或有意义的值)。而不是提供给Queue并让其他一些线程取消它(或者在你的情况下向ExecutorService提供服务并让服务维护一个脱掉内部工作队列的线程池),您向Pipelineable提供PipelineManager(也称为任务),该ExecutorService找到该任务密钥的正确队列,并将任务粘贴到该队列上。有各种其他代码可以管理脱离队列的线程,以确保始终只有1个线程从该队列中取出,以保证为同一个键提供的所有工作都将按顺序执行。

使用这种方法,您可以轻松地为n组串行工作留出某些键,同时对可以按任何旧顺序进行的工作的剩余键进行轮循,或者您可以通过明智的键保持某些管道(线程)热选择。

这种方法对于JDK BlockingQueue实现是不可行的,因为它们由单个ThreadPoolExecutor支持(至少是singleThreadExecutor),因此没有办法说“做”这项工作在任何旧的顺序,但这项工作必须序列化“。我假设你当然希望保持吞吐量,否则根据danben的评论将所有内容都放在ExecutorService上。

(编辑)

为了保持相同的抽象,你可以做的是创建你自己的ThreadPoolExecutor实现,它根据你的需要委托给public class PipeliningExecutorService<T extends Pipelineable> implements ExecutorService { private Map<Key, ExecutorService> executors; private ExecutorService generalPurposeExecutor; // ExecutorService methods here, for example @Override public <T> Future<T> submit(Callable<T> task) { Pipelineable pipelineableTask = convertTaskToPipelineable(task); Key taskKey = pipelineable.getKey(); ExecutorService delegatedService = executors.get(taskKey); if (delegatedService == null) delegatedService = generalPurposeExecutor; return delegatedService.submit(task); } } public interface Pipelineable<K,V> { K getKey(); V getValue(); } (或类似)的多个实例; 1由n个线程和1个或多个单线程实例支持。像下面这样的东西(根本不是工作代码,但希望你能得到这个想法!)

ExecutorService

为此目的,这是非常丑陋的,{{1}}方法是通用的而不是服务本身,这意味着你需要一些标准的方法来编组传递给Pipelineable的任何内容,如果可以的话,还需要后备。 t(例如将其扔到通用池中)。

答案 3 :(得分:0)

嗯,我想到了一些东西,不太确定这是否有用,但也许它会(未经测试的代码)。这会超过细微之处(异常处理,取消,对底层执行程序的其他任务的公平性等),但可能有用。

 class SequentialExecutorWrapper implements Runnable
 {
     final private ExecutorService executor;

     // queue of tasks to execute in sequence
     final private Queue<Runnable> taskQueue = new ConcurrentLinkedQueue<Runnable>();

     // semaphore for pop() access to the task list
     final private AtomicBoolean taskInProcess = new AtomicBoolean(false);

     public void submit(Runnable task)
     {
         // add task to the queue, try to run it now
         taskQueue.offer(task);
         if (!tryToRunNow())
         {
             // this object is running tasks on another thread
             // do we need to try again or will the currently-running thread
             // handle it? (depends on ordering between taskQueue.offer()
             // and the tryToRunNow(), not sure if there is a problem)
         }
     }

     public void run()
     {
         tryToRunNow();
     }

     private boolean tryToRunNow()
     {
         if (taskInProcess.compareAndSet(false, true))
         {
             // yay! I own the task queue!
             try {
                 Runnable task = taskQueue.poll();
                 while (task != null)
                 {
                     task.run();
                     task = taskQueue.poll();
                 }
             }
             finally
             {
                 taskInProcess.set(false);
             }
             return true;
         }
         else
         {
             return false;
         }
     }