1 概述
当需要解决的问题可以表述为:
if (problem < SIZE) {
// solve directly
}else{
// divide the question
// solve the sub question
// solve the sub question
// ...
// join questions
}
就可以使用fork-join框架去解决.
fork-join具有 工作密取(work steal) 特性, 即每个线程会维护一个双端队列, 默认是从尾
部取任务, 当一个线程的工作队列为空时, 它会去其他线程的工作线程尾部窃取一个工作任务. 这种
机制可以让线程的限制时间减少, 提升程序效率, 并且减少获取工作任务的阻塞时间.
2 基本的使用方法
有两种方式去生命一个fork-join任务. 一种是RecursiveAction, 是不需要返回值的; 另一种是
RecursiveTask, T是它的返回值.
使用fork-join的步骤:
1. 集成RecursiveAction或RecursiveTask, 重写call方法;
2. 将RecursiveAtcion或RecursiveTask提交给ForkJoinPool的实例;
下面有一个简单的使用例子(引用自<
import java.util.concurrent.*;
/**
* This program demonstrates the fork-join framework.
* 程序功能:计算一个元素集里面多少个元素符合指定条件
* fork-in框架适合可以分割为更小问题的算法
* @author Cay Horstmann
* @version 1.00 2012-05-20
*/
public class ForkJoinTest {
public static void main(String[] args) {
final int SIZE = 10000000;
double[] numbers = new double[SIZE];
for (int i = 0; i < SIZE; i++){
numbers[i] = Math.random();
}
// 开始计算
Counter counter = new Counter(numbers, 0, numbers.length,
new Filter() {
public boolean accept(double x) {
return x > 0.5;
}
});
ForkJoinPool pool = new ForkJoinPool();
pool.invoke(counter);
System.out.println(counter.join());
}
}
interface Filter {
boolean accept(double t);
}
class Counter extends RecursiveTask<Integer> {
public static final int THRESHOLD = 1000;
private double[] values;
private int from;
private int to;
private Filter filter;
public Counter(double[] values, int from, int to, Filter filter) {
this.values = values;
this.from = from;
this.to = to;
this.filter = filter;
}
protected Integer compute() {
if (to - from < THRESHOLD) {
//当元素个数小于THRESHOLD时, 直接计算
int count = 0;
for (int i = from; i < to; i++) {
if (filter.accept(values[i])) count++;
}
return count;
} else {
//否则, 分割成两个子问题, 分别进行计算
int mid = (from + to) / 2;
Counter first = new Counter(values, from, mid, filter);
Counter second = new Counter(values, mid, to, filter);
//使用invokeAll来提交子任务
invokeAll(first, second);
//使用join来获取子任务的计算结果
return first.join() + second.join();
}
}
}
注:
提交子任务的方法有invokeAll和fork. fork是开始一个子线程去执行这个任务, 而invokeAll则是
开始n-1个线程去执行任务, 但保留一个任务自己执行. 相对于fork方法, invokeAll更加节省线程
资源.
3 哪些东西使用了它
java 8 的stream框架的并行化stream的底层正是fork-join.