多线程最佳实践:约束任务newFixedThreadPool

时间:2012-07-18 01:35:08

标签: java multithreading concurrency threadpool

我想在+ -42Mio记录的数据库上运行许多任务。我想分批运行5000个记录/次(结果850个任务)。 我也想限制线程数(到16)java开始为我做这个,我正在使用当前代码来完成这个任务:

 ExecutorService executorService = Executors.newFixedThreadPool(16);
 for (int j = 1; j < 900 + 1; j++) {
     int start = (j - 1) * 5000;
     int stop = (j) * 5000- 1;
     FetcherRunner runner = new FetcherRunner(routes, start, stop);
     executorService.submit(runner);

     Thread t = new Thread(runner);
     threadsList.add(t);
     t.start();
 }

这是正确的方法吗?特别是因为我的印象是java只会触发所有任务...(FetcherRunner实现runnable

5 个答案:

答案 0 :(得分:4)

使用ExecutorService的第一部分看起来不错:

...
FetcherRunner runner = new FetcherRunner(routes, start, stop);
executorService.submit(runner);

有线程的部分不应该在那里,我假设你在那里只是为了展示你以前如何拥有它?

<强>更新 是的,您不需要executorService.submit(runner)之后的代码,这将最终产生大量的线程。如果您的目标是在循环后等待所有提交的任务完成,那么您在提交任务时可以获得对Future的引用并等待Future,如下所示:

ExecutorService executorService = Executors.newFixedThreadPool(16);
List<Future<Result>> futures = ..;
 for (int j = 1; j < 900+ 1; j++) {
 int start = (j - 1) * 5000;
 int stop = (j) * 5000- 1;
 FetcherRunner runner = new FetcherRunner(routes, start, stop);
 futures.add(executorService.submit(runner));

}
for (Future<Result> future:futures){
    future.get(); //Do something with the results..
}

答案 1 :(得分:4)

  

这是正确的工作方式吗?

第一部分是正确的。但是你不应该创建和启动新的Thread对象。提交Runnable时,ExecutorService会将其放在队列中,然后在工作线程可用时运行它。

  

....我使用threadlist检测我的所有线程何时完成,以便我可以继续处理结果。

如果您正在执行当前正在执行的操作,那么您将运行两次任务。更糟糕的是,手动创建的线程群将全部尝试并行运行。

确保所有任务都已完成的一种简单方法是在ExecutorService上调用awaitTermination(...)。 (有序关闭执行程序服务将产生相同的效果......如果您不打算再次使用它。)

另一种方法是为每个Future的结果创建FetcherRunner,并在提交完所有任务后尝试get结果。这样做的好处是,您可以在生成后期结果之前开始处理早期结果。 (但是,如果你不需要......或者不能......这样做,使用Futures将无法实现任何目标。)

答案 2 :(得分:3)

在电话提交后,您不需要该部分。您创建线程的代码将导致创建900个线程! Yowza。 ExecutorService有一个包含16个线程的池,您可以一次运行16个作业。当所有16个线程都忙时提交的任何作业都将排队。来自文档:

  

创建一个重用固定数量的线程的线程池   共享       无界队列。在任何时候,最多nThreads线程将是活动的处理任务。       如果在所有线程都处于活动状态时提交了其他任务,则它们将等待       队列直到线程可用。如果任何线程由于故障期间终止而终止       在关机之前执行,如果需要执行,新的将取代它       后续任务。池中的线程将一直存在,直到明确关闭为止。

所以不需要另一个线程。如果您需要在任务完成后收到通知,可以让它呼出。其他选项是缓存从提交返回的所有Future,并且在完成每个任务后,您可以检查是否所有Future已完成。完成所有未来之后,您可以发送另一个功能来运行。但它将在ExecutorService中的一个线程上运行。

答案 3 :(得分:0)

从您的代码更改:

    ExecutorService executorService = Executors.newFixedThreadPool(16);
    for (int j = 1; j < 900 + 1; j++) {
        int start = (j - 1) * 5000;
        int stop = (j) * 5000 - 1;
        FetcherRunner runner = new FetcherRunner(routes, start, stop);
        executorService.submit(runner);

    }

答案 4 :(得分:0)

最好的方法是使用countdownlatch如下

    ExecutorService executorService = Executors.newFixedThreadPool(16);
  CountdownLatch latch = new CountdownLatch(900);
 FetcherRunner runner = new FetcherRunner(routes, start, stop, latch);
 latch.await();

在最终块下的FetcherRunner中,只有在完成所有任务后才会执行latch.countDown();之后的await()代码。