Java并发包---forkjoin-RecursiveAction

RecursiveAction继承自ForkJoinTask,代表一个异步执行的结果,是没有返回值的任务。
而RecursiveTask是有返回值的任务,这两个共同组成ForkJoin框架的任务。

一个简单的例子

以下是java API中给出的RecursiveAction的例子,将给定数组的每个元素都自增:

public class IncrementTask extends RecursiveAction {
    public static final double THRESHOLD = 4;
    final long[] array; final int lo, hi;
    IncrementTask(long[] array, int lo, int hi) {
        this.array = array; this.lo = lo; this.hi = hi;
    }
    protected void compute() {
        if (hi - lo < THRESHOLD) {
            for (int i = lo; i < hi; ++i)
                array[i]++;
        } else {
            int mid = (lo + hi) >>> 1;
            invokeAll(new IncrementTask(array, lo, mid),
                    new IncrementTask(array, mid, hi));
        }
    }

    public static void main(String[] args) {
        ForkJoinPool pool = new ForkJoinPool();
        long[] array = new long[]{11,1,4,5,6,7,9};
        pool.invoke(new IncrementTask(array,0,array.length-1));
        for (int i = 0; i < array.length; i++) {
            System.out.println(array[i]);
        }
    }
}

其中重写的compute方法中的invokeAll()方法中执行并行的逻辑,这是使用分治的思想。

但这个方法并没有涉及fork() 和 join()方法,所以不太典型。

体现Fork/Join的例子

 double sumOfSquares(ForkJoinPool pool, double[] array) {
   int n = array.length;
   Applyer a = new Applyer(array, 0, n, null);
   pool.invoke(a);
   return a.result;
 }

 class Applyer extends RecursiveAction {
   final double[] array;
   final int lo, hi;
   double result;
   Applyer next; // keeps track of right-hand-side tasks
   Applyer(double[] array, int lo, int hi, Applyer next) {
     this.array = array; this.lo = lo; this.hi = hi;
     this.next = next;
   }

   double atLeaf(int l, int h) {
     double sum = 0;
     for (int i = l; i < h; ++i) // perform leftmost base step
       sum += array[i] * array[i];
     return sum;
   }

   protected void compute() {
     int l = lo;
     int h = hi;
     Applyer right = null;
     while (h - l > 1 && getSurplusQueuedTaskCount() <= 3) {
       int mid = (l + h) >>> 1;
       right = new Applyer(array, mid, h, right);
       right.fork();
       h = mid;
     }
     double sum = atLeaf(l, h);
     while (right != null) {
       if (right.tryUnfork()) // directly calculate if not stolen
         sum += right.atLeaf(right.lo, right.hi);
       else {
         right.join();
         sum += right.result;
       }
       right = right.next;
     }
     result = sum;
   }
 }

这个例子是求给定数组元素的平方和,深入compute方法中可以看到先是将数组利用分治的方法进行分割执行,然后从叶子节点进行计算,最后通过一个循环累加所有右侧片段的平方和。

从这个例子可以看出RecursiveAction不是必须完全递归的,只要我们使用分治的思想就可以充分的利用fork/joinu框架来加速计算过程,通常这样的方式比递归更加高效,因为递归会产生很多重复的计算。

例如计算斐波那契数的递归算法就包含了很多重复的计算过程,反而for循环迭代的计算方式效果更好。

你可能感兴趣的:(java)