简体   繁体   English

在使用单线程执行器时指定任务的执行顺序

[英]Specify order of execution of task while using single threaded executor

I currently have a bunch of tasks that I want to execute.我目前有一堆要执行的任务。 I am using the single-threaded executor in java.我在 java 中使用单线程执行器。 These are mainly of 2 types.这些主要有2种。 Let's call these types TaskA and TaskB.我们将这些类型称为 TaskA 和 TaskB。 I have 10 tasks of type TaskA and 5 tasks of type TaskB.我有 10 个 TaskA 类型的任务和 5 个 TaskB 类型的任务。 I have to execute them all but I cannot control the sequence in which they are submitted to the executor.我必须全部执行它们,但我无法控制它们提交给执行者的顺序。 A few tasks of type TaskB might be submitted to the executor before all 10 tasks of type TaskA have been submitted.在所有 10 个 TaskA 类型的任务都已提交之前,可能会将一些 TaskB 类型的任务提交给执行程序。 Is there any way to ensure that all 10 tasks of type TaskA are executed before the 5 tasks of type TaskB?有没有什么办法可以保证TaskA类型的10个任务全部在TaskB类型的5个任务之前执行? In order to successfully execute all tasks of type TaskB, I need to first execute all tasks of type TaskA.为了成功执行TaskB类型的所有任务,我需要先执行TaskA类型的所有任务。 You may think of TaskA tasks to be responsible for data loading and TaskB tasks for data processing.您可能会认为 TaskA 任务负责数据加载,而 TaskB 任务负责数据处理。 Without loading the data I cannot process it and run into exceptions如果不加载数据,我无法处理它并遇到异常

Please do let me know if I can phrase the question better if it is unclear如果不清楚,请告诉我是否可以更好地表达问题

No, the default executor service implementations do not differentiate between submitted tasks.不,默认的执行器服务实现不区分提交的任务。

You need a task manager object.你需要一个任务管理器 object。

Define a class that contains the single-threaded executor service as a member field.定义一个包含单线程执行器服务作为成员字段的 class。 That class offers methods submit( TaskA ta ) and submit( TaskB tb ) . class 提供方法submit( TaskA ta )submit( TaskB tb )

The second method collects the B tasks, as a queue, holding them for now if we've not yet processed ten A tasks.第二种方法收集 B 任务,作为一个队列,如果我们还没有处理十个 A 任务,则暂时保留它们。

The first method accepts each A task, submitting to the executor service immediately.第一种方法接受每个 A 任务,立即提交给 executor 服务。 And the first method counts those submissions.第一种方法计算这些提交。 On the tenth A task, flag is set, and all stored B tasks are submitted to the member field executor service.第10个A任务,设置flag,将所有存储的B任务提交给成员字段执行器服务。

The second method always checks for that “A tasks done” flag being set.第二种方法总是检查是否设置了“A tasks done”标志。 If set, any further B tasks submissions are sent directly to the executor service.如果设置,任何进一步的 B 任务提交都将直接发送到执行器服务。

Your task manager class could itself implement the ExecutorService interface.您的任务管理器 class 本身可以实现ExecutorService接口。 But I don't know if I would go that far.但我不知道我是否会 go 那么远。

The way I think you could do this is using the semaphore/locking pattern.我认为你可以做到这一点的方式是使用信号量/锁定模式。

first, you need a lock.首先,你需要一把锁。 You can use an object您可以使用 object

Object lock = new Object();

Then you need a count of how many A tasks have completed.然后,您需要计算完成了多少 A 任务。

int completedAs = 0; // variable name is completed 'A's, not 'as'

Both of these should be static or otherwise available to TaskA and TaskB.这两个都应该是 static 或 TaskA 和 TaskB 可用的其他方式。 Then what you can do is only add the TaskB's to the ExecutorService when the appropriate number of TaskA's have completed, like然后你可以做的只是在适当数量的TaskA完成后将TaskB添加到ExecutorService,比如

for (TaskB : allTaskBs) {
  synchronized(lock) {
    //add the taskB to the ExecutorService
  }
}

And then upon taskA completion:然后在 taskA 完成后:

synchronized(lock) {
  completedAs++;
  if (...) {
    lock.notify(); // you can call this multiple times to release multiple B's
  }
}

Here is something of a weird solution.这是一个奇怪的解决方案。 Provided you have a default executor.前提是你有一个默认的执行者。

ExecutorService service = Executors.newSingleThreadExecutor();

Your need to keep track of how many a tasks have completed and how many need to be completed.您需要跟踪已完成的任务数量以及需要完成的任务数量。

AtomicInteger a = new AtomicInteger(0);
int totalA = 10;

Then for submitting a task.然后提交任务。

void submitTask(Runnable t){
    Runnable r = ()->{
        if( t instance of TaskA ){
            try{
                t.run();
            } finally{
                a.incrementAndGet();
            }
        } else if( t instance of TaskB ){
            if( a.get() >= totalA ){
                t.run();
            } else{
                service.submit(this);
            }
        } else{
            throw new RuntimeException("not an acceptable task!");
        }

    }
    service.submit(r);
}

This will filter the TaskA's and the TaskB's, TaskA's will be immediately executed, but TaskB's will be resubmitted.这将过滤TaskA和TaskB,TaskA将立即执行,但TaskB将重新提交。

There are some flaws to this design.这种设计存在一些缺陷。 I think ThreadPoolExecutor can be setup a little better where you reject a task if it is not ready to be run.我认为ThreadPoolExecutor可以设置得更好一点,如果它还没有准备好运行,你会拒绝一个任务。

I suspect that you could design your setup a little better.我怀疑您可以更好地设计您的设置。 They have tools like an ExecutionCompletionService, or CountDownLatch that are made for creating barriers to execution.他们拥有 ExecutionCompletionService 或 CountDownLatch 等工具,用于创建执行障碍。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM