假设我有以下代码,我不想通过将工作负载分散到我的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
。我更喜欢一种仅限于创建正确的线程池的解决方案。
答案 0 :(得分:1)
我遇到麻烦,让
thread
知道array
中应该存储结果的地方ThreadFactory
。我应该定义自己的Runnable
吗?
没必要。执行程序(Callable
和ThreadPoolExecutor
)使用的接口由线程运行,您可以将任何参数传递给要传递的实现(例如,数组,开始索引和结束索引)
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;队列供主人拿起和处理。