简体   繁体   English

Java线程wait()和notify()与并行任务

[英]Java Thread wait() & notify() with parallel task

I have a requirement where in a single method call I want to hit three different collections to retrieve data. 我有一个要求,在单个方法调用中,我想命中三个不同的集合以检索数据。 Right now I am doing one after another to get data. 现在,我要一个接一个地获取数据。 Instead, I want to create three threads where each should execute in parallel. 相反,我想创建三个线程,每个线程应并行执行。 Then after every thing is completed, I want to merge the results and send it to the client. 然后,在完成所有操作之后,我要合并结果并将其发送给客户端。

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

Can any one help me to solve the problem? 谁能帮助我解决问题?

Pass same CyclicBarrier to all threads. 将相同的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);

and then in each of three threads, 然后在三个线程中的每个线程中

this.barrier.await();

this ensures all threads wait until all other threads(which have same cyclic barrier instruction) hits this line, then program flow continues for them and they can repeat this process. 这样可以确保所有线程都等到所有其他线程(具有相同的循环屏障指令)都到达此行,然后继续为其执行程序流,并且他们可以重复此过程。

You dont even need explicit synchronization. 您甚至不需要显式同步。 But this is slow for many threads. 但这对于许多线程来说很慢。 If you have several threads, its ok. 如果您有多个线程,就可以了。

To make it easier, you can count main thread as 4th thread and call 为了简化操作,您可以将主线程计为第4个线程并调用

meetingPoint.await();

in it. 在里面。 But it needs to be instantiated with 1 extra thread as 但是它需要用1个额外的线程实例化为

 CyclicBarrier meetingPoint=new CyclicBarrier(4);

An ExecutorService provides robust, high-level support for general purpose parallel processing. ExecutorService为通用并行处理提供了强大的高级支持。 Here, I'm assuming your tasks are different methods ( task1() , task2() , …) provided by some service ( svc ) that return records, and the result is the union of these results. 在这里,我假设您的任务是由返回记录的某些服务( svc )提供的不同方法( task1()task2() …),并且结果是这些结果的并集。

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());
}

Exceptions would need to be handled; 异常需要处理; here, I'm specifying a 10-second timeout. 在这里,我指定了10秒超时。 If some results are not ready in that time, a CancellationException is thrown. 如果在那时还没有准备好某些结果,则抛出CancellationException If another thread cancels this request by interrupting this thread, InterruptedException is thrown, and if a task fails, ExecutionException is thrown. 如果另一个线程通过中断该线程取消了该请求,则抛出InterruptedException ,并且如果任务失败,则抛出ExecutionException In all of these cases, it's likely that you'd respond to the client in the same way, with an "internal error" message. 在所有这些情况下,您都可能以相同的方式响应客户端,并显示“内部错误”消息。

In this example, the ExecutorService is created and destroyed with every request, which is costly. 在此示例中, ExecutorService随每个请求创建并销毁,这是很昂贵的。 In an application with a well-defined lifecycle defining when the service could be created and destroyed, it could be reused for all requests. 在具有明确定义的生命周期的应用程序中,该应用程序定义了何时可以创建和销毁服务,可以将其重用于所有请求。 In that case, you may want a cached thread pool instead of a fixed thread pool. 在这种情况下,您可能需要缓存的线程池而不是固定的线程池。

The newer ForkJoinPool , or the parallel Stream functionality built on fork-join could also be used, but it's suitable for a narrower set of tasks. 也可以使用更新的ForkJoinPool或在fork-join上构建的并行 Stream功能,但它适用于一组较窄的任务。 If the tasks don't block or throw checked exceptions, Stream is concise. 如果任务没有阻止或引发检查的异常,则Stream简明扼要。 If the tasks are time-consuming but can easily be recursively sub-divided into smaller tasks, ForkJoinPool might be worth using. 如果任务很耗时,但可以轻松地将其细分为较小的任务,那么ForkJoinPool可能值得使用。 Here's an example using a Stream : 这是使用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());
}

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

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