通过使用线程池多线程处理for循环中完成的工作

时间:2014-03-12 19:10:45

标签: java multithreading for-loop executorservice threadpoolexecutor

假设我有以下代码,我不想通过将工作负载分散到我的PC的多个CPU内核来优化:

double[] largeArray = getMyLargeArray();
double result = 0;
for (double d : largeArray)
    result += d;
System.out.println(result);

在这个例子中,我可以在多个线程上分发for循环中完成的工作,并在继续打印result之前验证线程是否已全部终止。因此,我想出了一些看起来像这样的东西:

final double[] largeArray = getMyLargeArray();
int nThreads = 5;
final double[] intermediateResults = new double[nThreads];

Thread[] threads = new Thread[nThreads];
final int nItemsPerThread = largeArray.length/nThreads;
for (int t = 0; t<nThreads; t++) {
    final int t2 = t;
    threads[t] = new Thread(){
        @Override public void run() {
            for (int d = t2*nItemsPerThread; d<(t2+1)*nItemsPerThread; d++)
                intermediateResults[t2] += largeArray[d];
        }
    };
}
for (Thread t : threads)
    t.start();
for (Thread t : threads)
    try {
        t.join();
    } catch (InterruptedException e) { }
double result = 0;
for (double d : intermediateResults)
    result += d;
System.out.println(result);

假设largeArray的长度可以nThreads分割。此解决方案正常工作。

但是,我遇到的问题是上面的for循环线程在我的程序中发生了很多,由于线程的创建和垃圾收集而导致很多开销。因此,我正在考虑使用ThreadPoolExecutor修改我的代码。给出中间结果的线程将在下一次执行中重复使用(在本例中为求和)。

由于我将中间结果存储在必须事先知道的大小数组中,因此我考虑使用固定大小的线程池。 但是,我遇到麻烦,让线程知道数组中应该存储结果的位置。 我应该定义自己的ThreadFactory吗?

或者我是否更好地使用方法ExecutorService创建的Executors.newSingleThreadExecutor(ThreadFactory myNumberedThreadFactory)数组?

请注意,在我的实际程序中,很难用其他类型的东西替换double[] intermediateResults。我更喜欢一种仅限于创建正确的线程池的解决方案。

3 个答案:

答案 0 :(得分:1)

  

我遇到麻烦,让thread知道array中应该存储结果的地方ThreadFactory。我应该定义自己的Runnable吗?

没必要。执行程序(CallableThreadPoolExecutor)使用的接口由线程运行,您可以将任何参数传递给要传递的实现(例如,数组,开始索引和结束索引)

FutureTask确实是一个很好的解决方案。如果您有可运行的结果,请查看{{1}}。

答案 1 :(得分:1)

ExecutorService为您提供API以通过Future接口从线程池中获取结果:

 Future<Double> futureResult = executorService.submit(new Callable<Double>() {
     Double call() {
         double totalForChunk = 0.0;
         // do calculation here
         return totalForChunk;
     }
 });

现在您需要做的就是提交任务(Callable个实例)并等待结果可用:

 List<Future<Double>> results = new ArrayList<Double>();
 for (int i = 0; i < nChunks; i++) {
     results.add(executorService.submit(callableTask));
 }

甚至更简单:

 List<Future<Double>> results = executorService.invokeAll(callableTaskList);

其余的很简单,迭代results并收集总数:

 double total = 0.0;
 for (Future<Double> result : results) {
     total += result.get(); // this will block until your task is completed by executor service
 }

说完了,你不关心你在执行服务中有多少线程。您只需提交任务并在可用时收集结果。

答案 2 :(得分:0)

你最好创造&#34;工人&#34;获取有关要从队列执行的工作的信息的线程。然后,您的过程将创建一个最初为空的WorkQueue,然后创建并启动工作线程。每个工作线程都会从队列中获取其工作,完成工作,并将工作放在&#34;完成&#34;队列供主人拿起和处理。