像web服务器这种用了大量的线程,不过大部分时间线程可能都是空闲的。另外有一些应用可能对每个处理器内核分别使用一个线程来完成计算密集型任务。比如图像或视频处理。这时候 就应当使用fork-join框架。假如有一个处理任务 可以自然的分成子任务如
if(problemsize<threshold){
solve problem
}
else
{
break problem into subproblems
}
这次我自然地想到了快排算法。因为这个常常会oom的问题。后来又写成了非递规。不过不知道能不能在这上实现。从网上找了一下貌似没人这么做过。我就试试吧。。
public class Demo {
public static void main(String[] Args) {
new Demo().test();
}
private void test() {
// TODO Auto-generated method stub
ForkJoinPool pool = new ForkJoinPool();
// int[] a = { 1, 2, 3, 4, 5, 56, 67, 65, 4, 3, 3 };
int[] a = new Produce_num().random_num();
int p = 0;
int r = a.length - 1;
long time = System.currentTimeMillis();
pool.invoke(new counter1(a, p, r));
for (int i : a) {
System.out.println(i);
}
System.out.println(System.currentTimeMillis() - time);
}
class counter1 extends RecursiveTask<Integer> {
public counter1(int[] a, int p, int r) {
super();
this.a = a;
this.p = p;
this.r = r;
}
int a[];
int p;
int r;
@Override
protected Integer compute() {
// TODO Auto-generated method stub
if (p < r) {
int q = partion(a, p, r);
counter1 count = new counter1(a, p, q - 1);
counter1 count2 = new counter1(a, q + 1, r);
invokeAll(count, count2);
}
return null;
}
int partion(int a[], int p, int r) {
int x = a[r];
int middle = p;
for (int j = p; j < r; j++) {
if (a[j] < x) {
int temp = a[middle];
a[middle] = a[j];
a[j] = temp;
middle++;
}
}
int temp = a[r];
a[r] = a[middle];
a[middle] = temp;
return middle;
}
}
}
成功 了
输出结果 :
...
....
..
9994
9995
9995
9996
9996
693
声明一下。 这是对10000个随机数进行排序花了693毫秒。这里给出那个生成随机数的类。这是平常用来生成测试数据的类
package test;
public class Produce_num {
private static int SIZE=10000;
public int[] random_num() {
int[] num = new int[SIZE];
for (int i = 0; i < num.length; i++) {
num[i] = (int) (Math.random() * SIZE);
}
return num;
}
public int[] increase_num() {
int[] num = new int[SIZE];
for (int i = 0; i < num.length; i++) {
num[i] = (int) (Math.random() * i);
}
return num;
}
public int[] decrease_num() {
int[] num = new int[SIZE];
for (int i = 0; i < num.length; i++) {
num[i] = (int) (Math.random() * (SIZE - i));
}
return num;
}
public static void test(String[] args) {
Produce_num p = new Produce_num();
Output(p.increase_num());
}
private static void Output(int[] decreaseNum) {
// TODO Auto-generated method stub
for (int i = 0; i < decreaseNum.length; i++) {
System.out.println(decreaseNum[i]);
}
}
}
我觉得时间稍长,所以又写了一个常规的来对比一下。
private void comparetest() {
int[] a = new Produce_num().random_num();
int p = 0;
int r = a.length - 1;
long time = System.currentTimeMillis();
QuickSort.nonRecrutSort(a);
for (int i : a) {
System.out.println(i);
}
System.out.println(System.currentTimeMillis() - time);
}
结果常规的才花了132毫秒。用了forkjoin框架居然时间又长了近6倍。。那么问题出在哪呢。。
后想了一下觉得是正常的。因为forkjoin的好处 其实 是平衡可用线程的工作负载,比单纯地调用快排算法肯定多了许多其它的步骤,所以慢一点是正常的。后来我又把数组长度加到了10w,这次它们之前的效率相差不到一半。相信这个数据应该会在工作量越大的时候差距越小,甚至反超。另外forkjoin是适应单机多核而产生的。我的电脑 是i5。双核四线程,这样应该会有四个线程吧。
后来我又将排序的数组长度增加到了1000万。这时候forkjoin框架的强大就开始显现出来了。
2584
1089
传统快排用了2.5秒,而用了forkjoin的只用了1秒。
其实上面的例子有一个问题,像这种不需要最后合并结果的任务,我们应该用RecursiveAction.。这个是没有返回结果的。