由于限制

时间:2015-10-28 20:02:12

标签: java multithreading synchronization phaser

我收到一个大文件,有N个条目。对于每个条目,我正在创建一个新线程。我需要等待所有N个线程终止。

一开始我使用的是Phaser,但它的实现仅限于65K方。所以,正在爆炸,因为N可能像100K。

然后,我尝试了CountDownLatch。这工作正常,非常简单的概念和非常简单的实现。但我不知道N的数量。

Phaser是我的解决方案,但是有这个限制。

有什么想法吗?

这篇文章是相关的: Flexible CountDownLatch?

3 个答案:

答案 0 :(得分:1)

听起来您要解决的问题是尽快处理大量任务并等待处理完成。

同时处理大量任务的问题在于它可能导致过多的上下文切换,并且会使您的计算机瘫痪,并使处理速度降低到某个(硬件相关的)并发线程数以上。这意味着您需要对正在执行的并发工作线程设置上限。

Phaser和CountDownLatch都是同步原语,它们的目的是为关键代码块提供访问控制,而不是管理并行执行。

在这种情况下,我会使用Executor service。它支持添加任务(以多种形式,包括Runnable)。

您可以使用Executors课轻松创建ExecutorService。我建议使用fixed size thread pool来实现20-100个最大线程 - 具体取决于您的任务的CPU密集程度。任务所需的计算能力越强,可以处理的并行线程数量越少,性能就越低。

有多种方法可以等待所有任务完成:

  • 收集Future方法返回的所有submit个实例,只需在所有这些实例上调用get即可。这可确保在循环完成时执行每个任务。
  • Shut down执行人服务和wait for all the submitted tasks to finish。此方法的缺点是您必须指定等待任务完成的最长时间。此外,它不太优雅,你并不总是想要关闭Executor,这取决于你是在写一个单一的应用程序还是后来继续运行的服务器 - 如果是服务器应用程序,你肯定不得不采用以前的方法。

最后,这是一个代码片段,说明了所有这些:

List<TaskFromFile> tasks = loadFileAndCreateTasks();
ExecutorService executor = Executors.newFixedThreadPool(50);

for(TaskFromFile task : tasks) {
    // createRunnable is not necessary in case your task implements Runnable
    executor.submit(createRunnable(task));
}

// assuming single-shot batch job
executor.shutdown();
executor.awaitTermination(MAX_WAIT_TIME_MILLIS, TimeUnit.MILLISECONDS);

答案 1 :(得分:1)

ReusableCountLatchCountDownLatch替代方案,也允许增量。

用法如下:

ReusableCountLatch latch = new ReusableCountLatch(); // creates latch with initial count 0
ReusableCountLatch latch = new ReusableCountLatch(10); // creates latch with initial count 10

latch.increment(); // increments counter

latch.decrement(); // decrement counter

latch.waitTillZero(); // blocks until counts falls to zero

boolean succeeded = latch.waitTillZero(200, MILLISECONDS); // waits for up to 200 milliseconds until count falls to zero

int count = latch.getCount(); // gets actual count

要使用它,只需将此gradle / maven依赖项添加到项目中:'com.github.matejtymes:javafixes:1.0'

可在此处找到更多详细信息:https://github.com/MatejTymes/JavaFixes

答案 2 :(得分:0)

使用AtomicInteger,您可以轻松实现相同目标。初始化为1并随每个新线程递增。一旦在工人和生产者中完成,减少并获得。如果零运行你的整理Runnable。