上篇文章:java多线程解说【拾壹】_并发容器
上文我们介绍了多线程编程中常用的并发容器,下面说一下多线程中常用的并发框架,主要有:
1.Fork/Join;
2.FutureTask
本文先说说Join/Fork框架
Fork/Join模式
Fork/Join框架是JDK7开始提供的用于并行还行任务的框架,原理就是把一个大任务拆分成若干个小任务同时执行,然后把执行结果汇总返回。
Fork/Join框架的核心是由下列两个类组成的:
ForkJoinPool:实现了ExecutorService接口和工作窃取算法(Work-Stealing Algorithm)。它管理工作者线程,并提供任务的状态信息,以及任务的执行信息。
ForkJoinTask:将在ForkJoinPool中执行的任务的基类。
ForkJoinWorkerThread:执行任务的线程
对于执行任务的Task,根据是否有返回结果的不同,决定需要继承下面2个类之一。
RecursiveAction:用于任务没有返回结果的场景。
RecursiveTask:用于任务有返回结果的场景。
一个例子
下面是一个使用Fork/Join框架的例子,用于统计一个文件夹中文件的个数。这里是判断,当文件夹下又出现文件夹时,则fork出一个子任务同时运行:
public class CountFileTask extends RecursiveTask<Integer> {
private static final long serialVersionUID = 1L;
private File file;
private int sum = 0;
public CountFileTask(File f) {
this.file = f;
}
public CountFileTask() {
}
@Override
protected Integer compute() {
Integer sum = 0;
File[] list = file.listFiles();
for (File f : list) {
if (f.isDirectory()) {
CountFileTask cft = new CountFileTask(f);
cft.fork();
sum += cft.join();
} else{
sum++;
}
}
return sum;
}
public Integer computeCommon(File file2){
File[] list = file2.listFiles();
for (File f : list) {
if (f.isDirectory()) {
computeCommon(f);
} else{
sum++;
}
}
return sum;
}
public static void main(String[] args) {
ForkJoinPool forkJoinPool = new ForkJoinPool();
CountFileTask task = new CountFileTask(new File("D:/my workspaces"));
long start = System.currentTimeMillis();
Future<Integer> result = forkJoinPool.submit(task);
try {
System.out.println(result.get());
long duration = System.currentTimeMillis() - start;
System.out.println("time cost: " + duration + " ms");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
Fork/Join框架的原理
ForkJoinPool内部维护了一个ForkJoinTask数组和ForkJoinWorkerThread数组。当执行fork方法时,先把子任务放到ForkJoinTask数组里面,然后在ForkJoinWorkerThread数组里唤醒或创建一个线程来执行它;执行join方法时,先判断当前线程的状态:
1.如果任务状态是已完成,则直接返回任务结果。
2.如果任务状态是被取消,则直接抛出CancellationException。
3.如果任务状态是抛出异常,则直接抛出对应的异常。