Fork/Join(分开/联合)

Fork/Join框架式ExecutorService接口的实现,帮助你充分利用多处理器。它被设计成能递归的分解成更小部分工作。目标是使用所有可用的处理能力来提升你的应用的处理能力。

与其他ExecutorService实现一样,fork/join框架在一个线程池中分发任务给工作线程。因为使用一个work-stealing算法是这个框架截然不同。那些已经处理完任务的工作线程可以从其他正忙的线程中抢断任务。

这个框架的核心是ForkJoinPool类,一个AbstractExecutorService 类的扩展。ForkJoinPool 实现了核心work-stealing算法的实现并可以执行ForkJoinPool流程。

基本用法

使用这个框架的第一步是编写执行一段工作的代码。你的代码应该跟一下的伪代码相似:

if (my portion of the work is small enough)
do the work directly
else
split my work into two pieces
invoke the two pieces and wait for the results

在ForkJoinTask子类中封装这些代码,通常使用更专业的类型的一个,不管是RecursiveTask(可返回一个结果)还是RecursiveAction。


在你的ForkJoinTask子类准备好后,创建一个对象负责处理所有的工作并将它穿给ForkJoinPool实例的invoke方法。

Blurring for Clarity(清晰的模糊化处理 暂时这样翻译)

为帮助理解这个框架式如何工作的,考虑一下例子。假如你想模糊一个图片。原始来源图片由一个整形数组表示,数组中的每个整形表示单一像素的颜色值。模糊后的目标图片也用一个整形数组表示,数组大小与源相同。

执行模糊化通过处理一次源数组的像素而数量完成。每个像素是周围像素的平均值(红色,绿色和蓝色成分求平均),结果就存入目标数组中。因为一个图片是一个很大的数组,这个处理会话费很长时间。你可以充分利用多处理器上的并发处理通过实现fork/join框架的抢断算法。下面是一个可能实现:

public class ForkBlur extends RecursiveAction {
private int[] mSource;
private int mStart;
private int mLength;
private int[] mDestination;

// Processing window size; should be odd.
private int mBlurWidth = 15;

public ForkBlur(int[] src, int start, int length, int[] dst) {
mSource = src;
mStart = start;
mLength = length;
mDestination = dst;
}

protected void computeDirectly() {
int sidePixels = (mBlurWidth - 1) / 2;
for (int index = mStart; index < mStart + mLength; index++) {
// Calculate average.
float rt = 0, gt = 0, bt = 0;
for (int mi = -sidePixels; mi <= sidePixels; mi++) {
int mindex = Math.min(Math.max(mi + index, 0),
mSource.length - 1);
int pixel = mSource[mindex];
rt += (float)((pixel & 0x00ff0000) >> 16)
/ mBlurWidth;
gt += (float)((pixel & 0x0000ff00) >> 8)
/ mBlurWidth;
bt += (float)((pixel & 0x000000ff) >> 0)
/ mBlurWidth;
}

// Reassemble destination pixel.
int dpixel = (0xff000000 ) |
(((int)rt) << 16) |
(((int)gt) << 8) |
(((int)bt) << 0);
mDestination[index] = dpixel;
}
}

...


现在你实现抽象方法compute(),这个方法将直接模糊处理或者将模糊分解成两个更小的任务。简单的数组长度threshold 帮助决定工作是执行还是分割。

protected static int sThreshold = 100000;

protected void compute() {
if (mLength < sThreshold) {
computeDirectly();
return;
}

int split = mLength / 2;

invokeAll(new ForkBlur(mSource, mStart, split, mDestination),
new ForkBlur(mSource, mStart + split, mLength - split,
mDestination));
}


如果先前的方法在RecursiveAction 类的子类中,那么设置任务在ForkJoinPool中运行是理所当然的,按照以下步骤调用:

1 创建一个任务表示要处理的所有工作
// source image pixels are in src
// destination image pixels are in dst
ForkBlur fb = new ForkBlur(src, 0, src.length, dst);

2 创建一个ForkJoinPool 来运行任务
ForkJoinPool pool = new ForkJoinPool();

3 运行任务
pool.invoke(fb);


对于全部源代码,包括额外的创建目标图片文件的源代码看ForkBlur例子。

标准实现

除了使用这种框架在一个多处理器系统上实现自定义算法来并发处理任务,在java SE中有许多一般有用的功能使用fork/join框架也实现了。一个这样的实现在java se8中介绍,被java.util.Arrays 类用于它的parallelSort()方法。这些方法与sort()相似,但是借助fork/join框架实现了并发处理。然而,这个框架通过这三个方法如何确切的起到杠杆作用的就超出本书范围,对于这个信息就查询JAVA API文档。

这个框架的另一个实现用于java.util.streams包中,是java se 8 版本调用的Project Lambda的部分。为了更多信息,请参考Lambda Expressions 小节
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值