Java并发包:Java Fork and Join using ForkJoinPool

来源:互联网 发布:php框架有哪些 编辑:程序博客网 时间:2024/05/23 18:33

文章译自: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之前,这篇文章是非常值得一读的。

0 0