Java Thread wait()&带并行任务的notify()

时间:2015-10-07 15:44:14

标签: java parallel-processing

我有一个要求,在单个方法调用中,我想要三个不同的集合来检索数据。现在我正在做一个接一个的数据。相反,我想创建三个线程,每个线程应并行执行。然后在完成每件事后,我想合并结果并将其发送给客户。

public List<String> getUserData() {
    // all the tasks should execute in parallel & finally
    // merge all the result & send to the client.
    task1(); 
    task2();
    task3();
}

任何人都可以帮我解决问题吗?

2 个答案:

答案 0 :(得分:0)

将相同的CyclicBarrier传递给所有线程。

// you have 3 threads,
CyclicBarrier meetingPoint=new CyclicBarrier(3);

// assigns their "barrier" variable to cyclic barrier
YourThreadGenerator thread1=new YourThreadGenerator(meetingPoint);
YourThreadGenerator thread2=new YourThreadGenerator(meetingPoint);
YourThreadGenerator thread3=new YourThreadGenerator(meetingPoint);

然后在三个线程中的每个线程中

this.barrier.await();

这确保所有线程都等待,直到所有其他线程(具有相同的循环屏障指令)都到达此行,然后程序流继续为它们执行,并且它们可以重复此过程。

你甚至不需要显式同步。但这对很多线程来说都很慢。如果你有几个线程,那就ok。

为了更容易,您可以将主线程计为第4个线程并调用

meetingPoint.await();

在里面。但它需要用1个额外的线程实例化

 CyclicBarrier meetingPoint=new CyclicBarrier(4);

答案 1 :(得分:0)

ExecutorService为通用并行处理提供强大的高级支持。在这里,我假设您的任务是由返回记录的某个服务(task1())提供的不同方法(task2()svc,...),结果是这些结果的并集

void getUserData1()
  throws InterruptedException, ExecutionException, CancellationException
{
  List<Callable<List<String>>> tasks = Arrays.asList(svc::task1, svc::task2, svc::task3);
  ExecutorService workers = Executors.newFixedThreadPool(tasks.size());
  List<Future<List<String>>> tickets;
  try {
    tickets = workers.invokeAll(tasks, 10, TimeUnit.SECONDS);
  }
  finally {
    workers.shutdown();
  }
  List<String> results = new ArrayList<>();
  for (Future<List<String>> ticket : tickets)
    results.addAll(ticket.get());
}

需要处理例外情况;在这里,我指定一个10秒的超时。如果在那段时间内没有准备好某些结果,则抛出CancellationException。如果另一个线程通过中断此线程取消此请求,则抛出InterruptedException,如果任务失败,则抛出ExecutionException。在所有这些情况下,您可能会以相同的方式回复客户端,并显示“内部错误”消息。

在此示例中,ExecutorService随每个请求一起创建和销毁,这是昂贵的。在具有明确定义的生命周期的应用程序中,定义何时可以创建和销毁服务,可以将其重用于所有请求。在这种情况下,您可能需要cached thread pool而不是固定的线程池。

也可以使用较新的ForkJoinPool或基于fork-join构建的parallel Stream功能,但它适用于较窄的任务集。如果任务没有阻止或抛出已检查的异常,Stream是简明扼要的。如果任务耗时但可以轻松地递归细分为较小的任务,ForkJoinPool可能值得使用。以下是使用Stream

的示例
List<String> getUserData2()
{
  Stream<Supplier<List<String>>> s = Stream.of(svc::task1, svc::task2, svc::task3);
  return s.parallel().map(Supplier::get).flatMap(List::stream).collect(Collectors.toList());
}