一 介绍
Fork/Join 模式有自己的适用范围。如果一个应用能被分解成多个子任务,并且组合多个子任务的结果就能够获得最终的答案,那么这个应用就适合用 Fork/Join 模式来解决。图 1 给出了一个 Fork/Join 模式的示意图,位于图上部的 Task 依赖于位于其下的 Task 的执行,只有当所有的子任务都完成之后,调用者才能获得 Task 0 的返回结果。
图 1. Fork/Join 模式示意图
可以说,Fork/Join 模式能够解决很多种类的并行问题。通过使用 Doug Lea 提供的 Fork/Join 框架,软件开发人员只需要关注任务的划分和中间结果的组合就能充分利用并行平台的优良性能。其他和并行相关的诸多难于处理的问题,例如负载平衡、同步等,都可以由框架采用统一的方式解决。这样,我们就能够轻松地获得并行的好处而避免了并行编程的困难且容易出错的缺点。
二 示例
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.List;
import java.util.ArrayList;
import java.io.File;
public class CalcFileSize
{
private final static ForkJoinPool forkJoinPool = new ForkJoinPool();
private static class CalcFileProcess extends RecursiveTask<Long>
{
/**
*
*/
private static final long serialVersionUID = 1L;
final File file;
public CalcFileProcess(final File theFile)
{
file = theFile;
}
@Override
public Long compute()
{
long size = 0;
if(file.isFile())
{
size = file.length();
}
else
{
final File[] children = file.listFiles();
if(children != null)
{
List<ForkJoinTask<Long>> tasks =
new ArrayList<ForkJoinTask<Long>>();
for(final File child : children)
{
if(child.isFile())
{
size += child.length();
}
else
{
tasks.add(new CalcFileProcess(child));
}
}
for(final ForkJoinTask<Long> task : invokeAll(tasks))
{
size += task.join();
}
}
}
return size;
}
}
public static void main(String[] args)
{
final long start = System.nanoTime();
final long total = forkJoinPool.invoke(
new CalcFileProcess(new File("D:/soft")));
final long end = System.nanoTime();
System.out.println("总大小: " + total);
System.out.println("耗时: " + (end - start)/1.0e9);
}
}