文章译自:http://tutorials.jenkov.com/java-util-concurrent/index.html
抽空翻译了一下这个教程的文章,后面会陆续放出,如有不妥,请批评指正。
转载请注明出处。
ForkJoinPool是在java7中增加的。ForkJoinPool类似于Java ExecutorService,但是有一个不同。ForkJoinPool使得把它的任务切分成为一些小的任务然后提交给ForkJoinPool执行是很容易的。任务可以被不断的切分成子任务,只要任务是可以被切分的。这也许听起来很抽象,因此在教程中我将解释ForkJoinPool的工作原理,以及它是如何拆分任务的。
Fork and Join Explained
在说ForkJoinPool之前,我想先说说fork and Join的一般工作原理。
Fork and join原理包含递归执行的的两步,下面是fork and join以下这两步。
- Fork
使用fork and join原理,一个任务可以被分成许多子任务,这些子任务可以不发执行。下面是图示:
通过将任务拆分成许多子任务,每个子任务就可以在多个cup上并行执行,或者在线程上并发执行。
如果给定的任务是足够大的,那么这个任务会被合理拆分成许多的子任务。拆分任务也是一种开销。因此对于少量的工作,这种开销也许比通过执行每个子任务来加速实现任务的花销更多(就是拆分任务得不偿失)。
有一个将任务拆分成多少子任务的限制,我们称这个限制为阈。拆分成多少任务由一个合理的阈值决定。这很大程度上依赖你需要完成那类的工作。
- join
当一任务被拆分成了不同的子任务后,任务会等待直到所有的子任务执行完毕。
一旦所有子任务执行完毕,任务会join(merge)所有的结果为一个结果。下面是图示:
当然,不是所有的任务都会有返回结果。
The ForkJoinPool
ForkJoinPool是一种特殊的线程池,被设计用来良好的进行fork-and-join task splitting工作,ForkJoinPool在java.util.package包中,因此全类名是java.util.concurrent.ForkJoinPool.
创建一个ForkJoinPool
使用ForkJoinPool的构造函数来创建它,传递一个参数的时候,指定的是你希望的并发级别。并发级别表示在任务传递给ForkJoinPool时,你想有多少线程或者cpu并发工作。下面是创建ForkJoinPool的实例:
ForkJoinPool forkJoinPool = new ForkJoinPool(4);
这个例子中创建了一个并发级别为4的ForkJoinPool。
提交任务给ForkJoinPool
你提交任务到ForkJoinPool类似于提交任务到ExecutorService。你可以提交两类任务。第一种是任务没有任何返回结果(an “action”),另一种是可以返回结果(a “task”)。RecursiveAction和RecursiveTask这个两个类代表这两种任务。下面的部分将阐述如何使用这两类任务以及如何提交他们。
RecursiveAction
RecursiveAction是没有返回值的任务。它仅仅做一些工作,例如,将数据写入磁盘,然后退出。
RecursiveAction也许仍需要将任务拆分成子任务,然后在不同的线程或者cup上执行。
你需要继承实现RecursiveAction。下面是一个例子:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.RecursiveAction;
public class MyRecursiveAction extends RecursiveAction {
private long workLoad = 0;
public MyRecursiveAction(long workLoad) {
this.workLoad = workLoad;
}
@Override
protected void compute() {
//if work is above threshold, break tasks up into smaller tasks
if(this.workLoad > 16) {
System.out.println("Splitting workLoad : " + this.workLoad);
List<MyRecursiveAction> subtasks =
new ArrayList<MyRecursiveAction>();
subtasks.addAll(createSubtasks());
for(RecursiveAction subtask : subtasks){
subtask.fork();
}
} else {
System.out.println("Doing workLoad myself: " + this.workLoad);
}
}
private List<MyRecursiveAction> createSubtasks() {
List<MyRecursiveAction> subtasks =
new ArrayList<MyRecursiveAction>();
MyRecursiveAction subtask1 = new MyRecursiveAction(this.workLoad / 2);
MyRecursiveAction subtask2 = new MyRecursiveAction(this.workLoad / 2);
subtasks.add(subtask1);
subtasks.add(subtask2);
return subtasks;
}
}
例子非常的简单。MyRecursiveAction虚构了一个workLoad作为构造函数的参数。如果workLoad高于特定阈值,工作将被拆分成许多子任务,如果workLoad低于特定阈值,工作将会由MyRecursiveAction自己执行。
你可以像下面这样安排一个myrecursiveaction执行:
MyRecursiveAction myRecursiveAction = new MyRecursiveAction(24);
forkJoinPool.invoke(myRecursiveAction);
RecursiveTask
RecursiveTask是一种有返回结果的任务。它也可以将任务拆分成许多小的任务,同时和并这些子任务的结果到一个结果。拆分和合并可能发生在几个层次上。下面是一个RecursiveTask例子:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.RecursiveTask;
public class MyRecursiveTask extends RecursiveTask<Long> {
private long workLoad = 0;
public MyRecursiveTask(long workLoad) {
this.workLoad = workLoad;
}
protected Long compute() {
//if work is above threshold, break tasks up into smaller tasks
if(this.workLoad > 16) {
System.out.println("Splitting workLoad : " + this.workLoad);
List<MyRecursiveTask> subtasks =
new ArrayList<MyRecursiveTask>();
subtasks.addAll(createSubtasks());
for(MyRecursiveTask subtask : subtasks){
subtask.fork();
}
long result = 0;
for(MyRecursiveTask subtask : subtasks) {
result += subtask.join();
}
return result;
} else {
System.out.println("Doing workLoad myself: " + this.workLoad);
return workLoad * 3;
}
}
private List<MyRecursiveTask> createSubtasks() {
List<MyRecursiveTask> subtasks =
new ArrayList<MyRecursiveTask>();
MyRecursiveTask subtask1 = new MyRecursiveTask(this.workLoad / 2);
MyRecursiveTask subtask2 = new MyRecursiveTask(this.workLoad / 2);
subtasks.add(subtask1);
subtasks.add(subtask2);
return subtasks;
}
}
这个例子和上面RecursiveAction的例子是类似的,不同的是有返回值。
MyRecursiveTask类继承自 RecursiveTask< Long> , RecursiveTask< Long> 意味着从任务返回的结果是一个长整形(Long)。
MyRecursiveTask的例子也是将任务拆分成多个子任务,然后使用fork()方法来执行这些子任务。
此外,这个例子通过调用每个子任务的join()方法,接收每个子任务返回的结果。子任务的结果会合并到一个总的结果,然后被返回。这种合并子任务结果的情况同时在递归过程中发生。
你可以像下面这样使用RecursiveTask:
MyRecursiveTask myRecursiveTask = new MyRecursiveTask(128);
long mergedResult = forkJoinPool.invoke(myRecursiveTask);
System.out.println("mergedResult = " + mergedResult);
ForkJoinPool 的评论文章
对于java7中的新增的ForkJoinPool,并不是每个都是高兴的。在搜索关于ForkJoinPool的体验和意见时,我遇到了下面的评论文章:
A Java Fork-Join Calamity(http://coopsoft.com/ar/CalamityArticle.html)。
在你打算在你的项目中使用ForkJoinPool之前,这篇文章是非常值得一读的。