Java并发包:Java Fork and Join using ForkJoinPool

文章译自: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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值