Java 使用共享ExecutorService作为任务队列,如何知道作业何时完成?
我有一个处理较大作业的JobService。作业动态细分为多个任务,任务还可能生成子任务等,因此无法预测作业的任务总数。每个任务通过Java 使用共享ExecutorService作为任务队列,如何知道作业何时完成?,java,concurrency,threadpool,Java,Concurrency,Threadpool,我有一个处理较大作业的JobService。作业动态细分为多个任务,任务还可能生成子任务等,因此无法预测作业的任务总数。每个任务通过ExecutorService.submit(…)排队运行。问题是我似乎必须为每个作业创建一个单独的ExecutorService,因为判断“作业队列”何时完成的唯一方法是使用ExecutorService.waitivetermination(…)。不过,这似乎效率低下,因为工作和服务之间存在差异 我正在寻找一些替代方案,我在考虑为每个作业使用原子整数。提交新任务
ExecutorService.submit(…)
排队运行。问题是我似乎必须为每个作业创建一个单独的ExecutorService,因为判断“作业队列”何时完成的唯一方法是使用ExecutorService.waitivetermination(…)
。不过,这似乎效率低下,因为工作和服务之间存在差异
我正在寻找一些替代方案,我在考虑为每个作业使用原子整数。提交新任务时递增,任务完成时递减。但我必须在0时进行投票,这看起来很混乱,还有一些异常处理混乱
似乎一定有更好的解决方案?提交返回一个未来对象,可用于等待任务完成。您可以跟踪这些任务,并添加一个递归阻塞直到所有子任务完成的方法。通过这种方式,您可以在任何需要的地方重用执行器
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
public class JobExecutor {
ExecutorService executorService = Executors.newFixedThreadPool(1);
private class Task implements Runnable {
private final String name;
private final Task[] subtasks;
private final ExecutorService executorService;
private volatile boolean started = false;
private Future<?> taskFuture;
// Separate list from subtasks because this is what you'll probably
// actually use as you may not be passing subtasks as constructor args
private final List<Task> subtasksToWaitOn = new ArrayList<Task>();
public Task(String name, ExecutorService executorService,
Task... subtasks) {
this.name = name;
this.executorService = executorService;
this.subtasks = subtasks;
}
public synchronized void start() {
if (!started) {
started = true;
taskFuture = executorService.submit(this);
}
}
public synchronized void blockTillDone() {
if (started) {
try {
taskFuture.get();
} catch (InterruptedException e) {
// TODO Handle
} catch (ExecutionException e) {
// TODO Handle
}
for (Task subtaskToWaitOn : subtasksToWaitOn) {
subtaskToWaitOn.blockTillDone();
}
} else {
// TODO throw exception
}
}
@Override
public void run() {
for (Task subtask : subtasks) {
subtask.start();
subtasksToWaitOn.add(subtask);
}
System.out.println("My name is: " + name);
}
}
void testSubmit() {
Task subsubTask1 = new Task("Subsubtask1", executorService);
Task subtask1 = new Task("Subtask1", executorService, subsubTask1);
Task subtask2 = new Task("Subtask2", executorService);
Task subtask3 = new Task("Subtask3", executorService);
Task job = new Task("Job", executorService, subtask1, subtask2,
subtask3);
job.start();
job.blockTillDone();
System.out.println("Job done!");
}
public static void main(String[] args) {
new JobExecutor().testSubmit();
}
}
Submit返回可用于等待任务完成的未来对象。您可以跟踪这些任务,并添加一个递归阻塞直到所有子任务完成的方法。通过这种方式,您可以在任何需要的地方重用执行器
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
public class JobExecutor {
ExecutorService executorService = Executors.newFixedThreadPool(1);
private class Task implements Runnable {
private final String name;
private final Task[] subtasks;
private final ExecutorService executorService;
private volatile boolean started = false;
private Future<?> taskFuture;
// Separate list from subtasks because this is what you'll probably
// actually use as you may not be passing subtasks as constructor args
private final List<Task> subtasksToWaitOn = new ArrayList<Task>();
public Task(String name, ExecutorService executorService,
Task... subtasks) {
this.name = name;
this.executorService = executorService;
this.subtasks = subtasks;
}
public synchronized void start() {
if (!started) {
started = true;
taskFuture = executorService.submit(this);
}
}
public synchronized void blockTillDone() {
if (started) {
try {
taskFuture.get();
} catch (InterruptedException e) {
// TODO Handle
} catch (ExecutionException e) {
// TODO Handle
}
for (Task subtaskToWaitOn : subtasksToWaitOn) {
subtaskToWaitOn.blockTillDone();
}
} else {
// TODO throw exception
}
}
@Override
public void run() {
for (Task subtask : subtasks) {
subtask.start();
subtasksToWaitOn.add(subtask);
}
System.out.println("My name is: " + name);
}
}
void testSubmit() {
Task subsubTask1 = new Task("Subsubtask1", executorService);
Task subtask1 = new Task("Subtask1", executorService, subsubTask1);
Task subtask2 = new Task("Subtask2", executorService);
Task subtask3 = new Task("Subtask3", executorService);
Task job = new Task("Job", executorService, subtask1, subtask2,
subtask3);
job.start();
job.blockTillDone();
System.out.println("Job done!");
}
public static void main(String[] args) {
new JobExecutor().testSubmit();
}
}
如果您在Java7(或者Java6上带有后端库),您可能需要考虑一个分支连接池来处理这类事情:
class MainTask extends RecursiveTask<Long> {
@Override
protected Long compute() {
SubTask subtask0 = new SubTask(0L);
SubTask subtask1 = new SubTask(1L);
SubTask subtask2 = new SubTask(2L);
SubTask subtask3 = new SubTask(3L);
SubTask subtask4 = new SubTask(4L);
SubTask subtask5 = new SubTask(5L);
subtask1.fork();
subtask2.fork();
subtask3.fork();
subtask4.fork();
subtask5.fork();
return subtask0.compute() +
subtask1.join() +
subtask2.join() +
subtask3.join() +
subtask4.join() +
subtask5.join();
}
}
class SubTask extends RecursiveTask<Long> {
private Long rawResult = null;
private Long expected = null;
public SubTask(long expected) {
this.expected = expected;
}
@Override
protected Long compute() {
return expected;
}
}
public static void main( String[] args )
{
ForkJoinPool forkJoinPool = new ForkJoinPool();
Long result = forkJoinPool.invoke(new MainTask());
System.out.println(result);
}
class MainTask扩展了RecursiveTask{
@凌驾
受保护的长计算(){
子任务subtask0=新的子任务(0L);
子任务subtask1=新的子任务(1L);
子任务subtask2=新的子任务(2L);
子任务subtask3=新的子任务(3L);
子任务subtask4=新的子任务(4L);
子任务subtask5=新的子任务(5L);
subtask1.fork();
子任务2.fork();
子任务3.fork();
子任务4.fork();
子任务5.fork();
返回子任务0.compute()+
subtask1.join()+
subtask2.join()+
子任务3.join()+
子任务4.join()+
子任务5.join();
}
}
类子任务扩展递归任务{
private Long rawResult=null;
private Long expected=null;
公共子任务(长期期望){
this.expected=expected;
}
@凌驾
受保护的长计算(){
预期收益;
}
}
公共静态void main(字符串[]args)
{
ForkJoinPool ForkJoinPool=新的ForkJoinPool();
Long result=forkJoinPool.invoke(newmaintask());
系统输出打印项次(结果);
}
显然,这有硬编码的子任务,但没有理由不能将参数传递给主任务,并使用它生成子任务。子任务本身不必都是相同的类型,但它们都应该扩展RecursiveTask。实际上,如果一个任务生成子任务(如上面的MainTask),那么至少有一个子任务应该直接调用“compute”(而不是fork和join),以便当前线程可以执行一些计算,并让其他线程执行其余的计算。如果您使用的是java7(或带有backport库的java6),您可能想考虑这样的事情:一个分支连接池:
class MainTask extends RecursiveTask<Long> {
@Override
protected Long compute() {
SubTask subtask0 = new SubTask(0L);
SubTask subtask1 = new SubTask(1L);
SubTask subtask2 = new SubTask(2L);
SubTask subtask3 = new SubTask(3L);
SubTask subtask4 = new SubTask(4L);
SubTask subtask5 = new SubTask(5L);
subtask1.fork();
subtask2.fork();
subtask3.fork();
subtask4.fork();
subtask5.fork();
return subtask0.compute() +
subtask1.join() +
subtask2.join() +
subtask3.join() +
subtask4.join() +
subtask5.join();
}
}
class SubTask extends RecursiveTask<Long> {
private Long rawResult = null;
private Long expected = null;
public SubTask(long expected) {
this.expected = expected;
}
@Override
protected Long compute() {
return expected;
}
}
public static void main( String[] args )
{
ForkJoinPool forkJoinPool = new ForkJoinPool();
Long result = forkJoinPool.invoke(new MainTask());
System.out.println(result);
}
class MainTask扩展了RecursiveTask{
@凌驾
受保护的长计算(){
子任务subtask0=新的子任务(0L);
子任务subtask1=新的子任务(1L);
子任务subtask2=新的子任务(2L);
子任务subtask3=新的子任务(3L);
子任务subtask4=新的子任务(4L);
子任务subtask5=新的子任务(5L);
subtask1.fork();
子任务2.fork();
子任务3.fork();
子任务4.fork();
子任务5.fork();
返回子任务0.compute()+
subtask1.join()+
subtask2.join()+
子任务3.join()+
子任务4.join()+
子任务5.join();
}
}
类子任务扩展递归任务{
private Long rawResult=null;
private Long expected=null;
公共子任务(长期期望){
this.expected=expected;
}
@凌驾
受保护的长计算(){
预期收益;
}
}
公共静态void main(字符串[]args)
{
ForkJoinPool ForkJoinPool=新的ForkJoinPool();
Long result=forkJoinPool.invoke(newmaintask());
系统输出打印项次(结果);
}
显然,这有硬编码的子任务,但没有理由不能将参数传递给主任务,并使用它生成子任务。子任务本身不必都是相同的类型,但它们都应该扩展RecursiveTask。实际上,如果一个任务生成子任务(如上面的MainTask),至少有一个子任务应该直接调用“compute”(而不是fork和join),以便当前线程可以执行一些计算,让其他线程执行其余的计算。您检查过了吗?您可以与一个值一起使用,该值将它与正在完成的
作业关联起来。请查看示例代码。我确实查看了CompletionService
,但它似乎不是我想要的,因为我不关心处理单个结果,我只想知道它们何时全部完成,因此poll()/take()一个接一个看起来很混乱,我真的只想知道completionQueue何时是空的。也许出于我的目的,我应该考虑扩展它。您看过ExecutorSerice的invokeAll方法了吗?我不确定这是否能满足您的需求,或者我最初使用的是invokeAll,但问题是我提交给invokeAll的一些任务反过来会提交更多的任务,我需要知道它们何时完成。您检查过了吗?您可以与一个值一起使用,该值将其与正在完成的作业关联。请查看