如何使用依赖任务运行并发作业?

时间:2015-04-14 21:26:39

标签: java concurrency java.util.concurrent

我有一个需要处理的情况

我有一个有send方法的类,例子

@Singleton
class SendReport {

 public void send() {}
}

从用户点击网页调用send方法,必须立即返回,但必须启动一系列需要时间的任务

send
 ->|
   | |-> Task1
 <-|      |
        <-|
          | 
        |-> Task2 (can only start when Task1 completes/throws exception)
        <-|
          | 
        |-> Task3 (can only start when Task2 completes/throws exception)
        <-|

我是Java并发世界的新手并正在阅读它。根据我的理解,我需要Executor Servicesubmit()一份工作(Task1)来处理并让Future返回继续。

我说错了吗?

我理解和设计的难点是 - 任何此类任务如何以及在何处处理异常? - 据我所知,我必须做些什么吗?

    ExecutorService executorService = Executors.newFixedThreadPool(1);
    Future futureTask1 = executorService.submit(new Callable(){
    public Object call() throws Exception {
        System.out.println("doing Task1");
        return "Task1 Result";
    }
    });
    if (futureTask1.get() != null) {
    Future futureTask2 = executorService.submit(new Callable(){
    public Object call() throws Exception {
        System.out.println("doing Task2");
        return "Task2 Result";
    }
    }
    ... and so on for Task 3

这是对的吗? 如果有,是否有更好的推荐方式?

谢谢

4 个答案:

答案 0 :(得分:1)

Dependent task execution 使用 Dexecutor 变得简单

免责声明:我是业主

这是一个例子,它可以很容易地运行下面的复杂图形,更多细节可以参考this

enter image description here

这里是an example

答案 1 :(得分:0)

您当前的方法无效,因为它会阻止您想要避免的完全完成。

future.get()是阻塞(); 所以在提交第一个任务之后,你的代码将等到它完成然后下一个任务将被提交,再次等待,所以单个线程逐个执行任务没有任何优势。

所以,如果有任何代码需要:

Future futureTask2 = executorService.submit(new Callable(){
    public Object call() throws Exception {
        futureTask1.get()
        System.out.println("doing Task2");
        return "Task2 Result";
    }
}

您的图表表明,除非有例外,否则后续任务应该执行。如果计算出现问题,将从get抛出ExecutionException,因此您需要通过适当的尝试来保护get()。

由于Task1,Task2必须一个接一个地完成,为什么你要在不同的线程中执行它们。为什么不用一个带有run方法的线程来逐个处理Task1,Task2 ..正如你所说的不是你的&#34;主要&#34;线程,它可以在执行程序作业中,但可以处理所有任务。

我个人不喜欢匿名的内部课程和回调(这就是你对期货链的模仿)。如果我必须实现任务序列,我实际上会实现任务队列和执行它们的处理器。

主要是因为它可以更易于管理,因为我可以监控队列的内容,甚至可以删除不必要的任务。

所以我会有BlockingQueue<JobDescription>我将提交JobDescription,其中包含执行任务所需的所有数据。

我将实现线程(处理器),它们在run()中将具有不定式循环,在该循环中,它们从队列中获取作业,执行任务,并将以下任务放回队列。这些方面的东西。

但是如果在send方法中预定义了Tasks,我只需将它们作为一个作业提交,然后在一个线程中执行。如果它们总是顺序的,则在不同的线程之间分割它们是没有意义的。

答案 2 :(得分:0)

如果您在完成上一个任务时需要调用一系列任务,而不是在前面的答案中说明和讨论过,我认为您根本不需要多个线程。

如果你有一个任务池,其中一些需要知道另一个任务的结果而其他人不关心你可以提出一个依赖的可调用实现。

public class DependentCallable implements Callable {

private final String name;
private final Future pre;

public DependentCallable(String name, Future pre) {
    this.name = name;
    this.pre = pre;
}

@Override
public Object call() throws Exception {
    if (pre != null) {
        pre.get();
        //pre.get(10, TimeUnit.SECONDS);
    }
    System.out.println(name);
    return name;
}

根据您问题中的代码,您需要处理的其他一些事情,如前面的回复中所述,在提交之间删除future.gets。使用的线程池大小至少大于callable之间的依赖关系深度。

答案 3 :(得分:0)

如果要立即返回发送请求,则需要再添加一项任务。请检查以下示例。它将请求提交给后台线程,后台线程将按顺序执行任务然后返回。

3个长时间运行任务的可调用对象。

public class Task1 implements Callable<String> {

    public String call() throws Exception {
        Thread.sleep(5000);
        System.out.println("Executing Task1...");
        return Thread.currentThread().getName();
    }
}

public class Task2 implements Callable<String> {

    public String call() throws Exception {
        Thread.sleep(5000);
        System.out.println("Executing Task2...");
        return Thread.currentThread().getName();
    }
}

public class Task3 implements Callable<String> {

    public String call() throws Exception {
        Thread.sleep(5000);
        System.out.println("Executing Task3...");
        return Thread.currentThread().getName();
    }
}

从客户端获取请求并立即返回,然后按顺序开始执行任务的主要方法。

public class ThreadTest {

    public static void main(String[] args) {
        final ExecutorService executorService = Executors.newFixedThreadPool(5);

        executorService.submit(new Runnable() {
            public void run() {
                try {
                    Future<String> result1 = executorService.submit(new Task1());
                    if (result1.get() != null) {
                        Future<String> result2 = executorService.submit(new Task2());
                        if (result2.get() != null) {
                            executorService.submit(new Task3());
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("Submitted request...");
    }

}