Controlling Task execution order with ExecutorService

前端 未结 8 1925
一生所求
一生所求 2020-12-02 14:30

I have a process which delegates asynch tasks to a pool of threads. I need to ensure that certain tasks are executed in order. So for example

Tasks arrive in order<

相关标签:
8条回答
  • 2020-12-02 14:53

    I have written my won executor service which is sequence aware. It sequences the tasks which contain certain related reference and currently inflight.

    You can go through the implementation at https://github.com/nenapu/SequenceAwareExecutorService

    0 讨论(0)
  • 2020-12-02 14:56

    When you submit a Runnable or Callable to an ExecutorService you receive a Future in return. Have the threads that depend on a1 be passed a1's Future and call Future.get(). This will block until the thread completes.

    So:

    ExecutorService exec = Executor.newFixedThreadPool(5);
    Runnable a1 = ...
    final Future f1 = exec.submit(a1);
    Runnable a2 = new Runnable() {
      @Override
      public void run() {
        f1.get();
        ... // do stuff
      }
    }
    exec.submit(a2);
    

    and so on.

    0 讨论(0)
  • 2020-12-02 15:00

    You can use Executors.newSingleThreadExecutor(), but it will use only one thread to execute your tasks. Another option is to use CountDownLatch. Here is a simple example:

    public class Main2 {
    
    public static void main(String[] args) throws InterruptedException {
    
        final CountDownLatch cdl1 = new CountDownLatch(1);
        final CountDownLatch cdl2 = new CountDownLatch(1);
        final CountDownLatch cdl3 = new CountDownLatch(1);
    
        List<Runnable> list = new ArrayList<Runnable>();
        list.add(new Runnable() {
            public void run() {
                System.out.println("Task 1");
    
                // inform that task 1 is finished
                cdl1.countDown();
            }
        });
    
        list.add(new Runnable() {
            public void run() {
                // wait until task 1 is finished
                try {
                    cdl1.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                System.out.println("Task 2");
    
                // inform that task 2 is finished
                cdl2.countDown();
            }
        });
    
        list.add(new Runnable() {
            public void run() {
                // wait until task 2 is finished
                try {
                    cdl2.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                System.out.println("Task 3");
    
                // inform that task 3 is finished
                cdl3.countDown();
            }
        });
    
        ExecutorService es = Executors.newFixedThreadPool(200);
        for (int i = 0; i < 3; i++) {
            es.submit(list.get(i));
        }
    
        es.shutdown();
        es.awaitTermination(1, TimeUnit.MINUTES);
    }
    }
    
    0 讨论(0)
  • 2020-12-02 15:00

    In Habanero-Java library, there is a concept of data-driven tasks which can be used to express dependencies between tasks and avoid thread-blocking operations. Under the covers Habanero-Java library uses the JDKs ForkJoinPool (i.e. an ExecutorService).

    For example, your use case for tasks A1, A2, A3, ... could be expressed as follows:

    HjFuture a1 = future(() -> { doA1(); return true; });
    HjFuture a2 = futureAwait(a1, () -> { doA2(); return true; });
    HjFuture a3 = futureAwait(a2, () -> { doA3(); return true; });
    

    Note that a1, a2, and a3 are just references to objects of type HjFuture and can be maintained in your custom data structures to specify the dependencies as and when the tasks A2 and A3 come in at runtime.

    There are some tutorial slides available. You can find further documentation as javadoc, API summary and primers.

    0 讨论(0)
  • 2020-12-02 15:02

    When I've done this in the past I've usually had the ordering handled by a component which then submits callables/runnables to an Executor.

    Something like.

    • Got a list of tasks to run, some with dependencies
    • Create an Executor and wrap with an ExecutorCompletionService
    • Search all tasks, any with no dependencies, schedule them via the completion service
    • Poll the completion service
    • As each task completes
      • Add it to a "completed" list
      • Reevaluate any waiting tasks wrt to the "completed list" to see if they are "dependency complete". If so schedule them
      • Rinse repeat until all tasks are submitted/completed

    The completion service is a nice way of being able to get the tasks as they complete rather than trying to poll a bunch of Futures. However you will probably want to keep a Map<Future, TaskIdentifier> which is populated when a task is schedule via the completion service so that when the completion service gives you a completed Future you can figure out which TaskIdentifier it is.

    If you ever find yourself in a state where tasks are still waiting to run, but nothing is running and nothing can be scheduled then your have a circular dependency problem.

    0 讨论(0)
  • 2020-12-02 15:09

    I write own Executor that warrants task ordering for tasks with same key. It uses map of queues for order tasks with same key. Each keyed task execute next task with the same key.

    This solution don't handle RejectedExecutionException or other exceptions from delegated Executor! So delegated Executor should be "unlimited".

    import java.util.HashMap;
    import java.util.LinkedList;
    import java.util.Map;
    import java.util.Queue;
    import java.util.concurrent.Executor;
    
    /**
    * This Executor warrants task ordering for tasks with same key (key have to implement hashCode and equal methods correctly).
    */
    public class OrderingExecutor implements Executor{
    
        private final Executor delegate;
        private final Map<Object, Queue<Runnable>> keyedTasks = new HashMap<Object, Queue<Runnable>>();
    
        public OrderingExecutor(Executor delegate){
            this.delegate = delegate;
        }
    
        @Override
        public void execute(Runnable task) {
            // task without key can be executed immediately
            delegate.execute(task);
        }
    
        public void execute(Runnable task, Object key) {
            if (key == null){ // if key is null, execute without ordering
                execute(task);
                return;
            }
    
            boolean first;
            Runnable wrappedTask;
            synchronized (keyedTasks){
                Queue<Runnable> dependencyQueue = keyedTasks.get(key);
                first = (dependencyQueue == null);
                if (dependencyQueue == null){
                    dependencyQueue = new LinkedList<Runnable>();
                    keyedTasks.put(key, dependencyQueue);
                }
    
                wrappedTask = wrap(task, dependencyQueue, key);
                if (!first)
                    dependencyQueue.add(wrappedTask);
            }
    
            // execute method can block, call it outside synchronize block
            if (first)
                delegate.execute(wrappedTask);
    
        }
    
        private Runnable wrap(Runnable task, Queue<Runnable> dependencyQueue, Object key) {
            return new OrderedTask(task, dependencyQueue, key);
        }
    
        class OrderedTask implements Runnable{
    
            private final Queue<Runnable> dependencyQueue;
            private final Runnable task;
            private final Object key;
    
            public OrderedTask(Runnable task, Queue<Runnable> dependencyQueue, Object key) {
                this.task = task;
                this.dependencyQueue = dependencyQueue;
                this.key = key;
            }
    
            @Override
            public void run() {
                try{
                    task.run();
                } finally {
                    Runnable nextTask = null;
                    synchronized (keyedTasks){
                        if (dependencyQueue.isEmpty()){
                            keyedTasks.remove(key);
                        }else{
                            nextTask = dependencyQueue.poll();
                        }
                    }
                    if (nextTask!=null)
                        delegate.execute(nextTask);
                }
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题