Fork/Join


This section was updated to reflect features and conventions of the upcoming Java SE 8 release. You can download the current JDK 8 snapshot from java.net.

The fork/join framework is an implementation of the ExecutorService interface that helps you take advantage of multiple processors. It is designed for work that can be broken into smaller pieces recursively. The goal is to use all the available processing power to enhance the performance of your application.

As with any ExecutorService implementation, the fork/join framework distributes tasks to worker threads in a thread pool. The fork/join framework is distinct because it uses a work-stealing algorithm. Worker threads that run out of things to do can steal tasks from other threads that are still busy.

The center of the fork/join framework is the ForkJoinPool class, an extension of the AbstractExecutorService class.ForkJoinPool implements the core work-stealing algorithm and can execute ForkJoinTask processes.

Basic Use

The first step for using the fork/join framework is to write code that performs a segment of the work. Your code should look similar to the following pseudocode:

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

Wrap this code in a ForkJoinTask subclass, typically using one of its more specialized types, either RecursiveTask (which can return a result) or RecursiveAction.

After your ForkJoinTask subclass is ready, create the object that represents all the work to be done and pass it to theinvoke() method of a ForkJoinPool instance.

Blurring for Clarity

To help you understand how the fork/join framework works, consider the following example. Suppose that you want to blur an image. The original source image is represented by an array of integers, where each integer contains the color values for a single pixel. The blurred destination image is also represented by an integer array with the same size as the source.

Performing the blur is accomplished by working through the source array one pixel at a time. Each pixel is averaged with its surrounding pixels (the red, green, and blue components are averaged), and the result is placed in the destination array. Since an image is a large array, this process can take a long time. You can take advantage of concurrent processing on multiprocessor systems by implementing the algorithm using the fork/join framework. Here is one possible implementation:

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;
}
} ...

Now you implement the abstract compute() method, which either performs the blur directly or splits it into two smaller tasks. A simple array length threshold helps determine whether the work is performed or split.

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));
}

If the previous methods are in a subclass of the RecursiveAction class, then setting up the task to run in a ForkJoinPool is straightforward, and involves the following steps:

  1. Create a task that represents all of the work to be done.

    // source image pixels are in src
    // destination image pixels are in dst
    ForkBlur fb = new ForkBlur(src, 0, src.length, dst);
  2. Create the ForkJoinPool that will run the task.

    ForkJoinPool pool = new ForkJoinPool();
    
  3. Run the task.

    pool.invoke(fb);
    

For the full source code, including some extra code that creates the destination image file, see the ForkBlur example.

Standard Implementations

Besides using the fork/join framework to implement custom algorithms for tasks to be performed concurrently on a multiprocessor system (such as the ForkBlur.java example in the previous section), there are some generally useful features in Java SE which are already implemented using the fork/join framework. One such implementation, introduced in Java SE 8, is used by the java.util.Arrays class for its parallelSort() methods. These methods are similar to sort(), but leverage concurrency via the fork/join framework. Parallel sorting of large arrays is faster than sequential sorting when run on multiprocessor systems. However, how exactly the fork/join framework is leveraged by these methods is outside the scope of the Java Tutorials. For this information, see the Java API documentation.

Another implementation of the fork/join framework is used by methods in the java.util.streams package, which is part ofProject Lambda scheduled for the Java SE 8 release. For more information, see the Lambda Expressions section.


译文:
分拆和合并


  这节是即将发布的Java Se 8中的反映的新特性。你能从java.net下载目前的Jdk 8 snapshot。


  分拆与合并框架是ExecutorService接口的实现帮助你有效利用多处理器。 他被设计为能够被打断成为细小的一片片的。这个目标是利用好处理器的所有能力来加强你的应用程序。
  像任何ExecutorService的应用一样,分拆和合并框架也是利用任务的工作线程的线程池。分拆和合并是唯一的因为它是利用的work-stealing算法。工作线程能够偷走任务从正在执行的其他线程哪里。
  分拆和合并框架的中心是ForkJoinPool类,它是AbstractExecutorService类的扩展。ForkJoinPool实现了work-stealing算法的核心并能执行ForkJoinTask进程。
基本使用
  用分拆和合并框架的第一步是写代码执行的工作段。你的代码可能像下面的伪码:

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()方法中。
为清楚的模糊(真费解!)
  为了让你理解分拆和合并框架的工作原理,考虑如下的例子。假如你想涂抹一幅图画,源图像被一个整数数组所表示,每一个整数包含颜色值代表一个像素。涂抹的目标图像也是用一个整数数组和原数组有相同的长度的数组所代表。执行涂抹是从源数组对象开始的。每一个像素代表了它周围的像素(红,绿,蓝的平均值),并且这个结果被放到了目标数组中。由于图片是一个大的数组,所以需要大量的时间来处理。你可以从多处理器通过实现分拆和合并框架算法实现的并发处理获益。这是一种实现:

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()方法,它在每次执行的时候都可以直接或者分开成两个小的任务。一个简单的数组长度阈值帮你确定是直接执行还是分割。

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.创建一个代表所有需要做的任务。
  2.创建能够运行这个任务的ForkJoinPool。
  3.运行这个任务。
  对于所有的源码,包括一些额外的源码创建目标图像,看ForkBlur实例。

import java.awt.image.BufferedImage;
import java.io.File;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import javax.imageio.ImageIO; /**
* ForkBlur implements a simple horizontal image blur. It averages pixels in the
* source array and writes them to a destination array. The sThreshold value
* determines whether the blurring will be performed directly or split into two
* tasks.
*
* This is not the recommended way to blur images; it is only intended to
* illustrate the use of the Fork/Join framework.
*/
public class ForkBlur extends RecursiveAction { private int[] mSource;
private int mStart;
private int mLength;
private int[] mDestination;
private int mBlurWidth = 15; // Processing window size, should be odd. public ForkBlur(int[] src, int start, int length, int[] dst) {
mSource = src;
mStart = start;
mLength = length;
mDestination = dst;
} // Average pixels from source, write results into destination.
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;
} // Re-assemble destination pixel.
int dpixel = (0xff000000)
| (((int) rt) << 16)
| (((int) gt) << 8)
| (((int) bt) << 0);
mDestination[index] = dpixel;
}
}
protected static int sThreshold = 10000; @Override
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));
} // Plumbing follows.
public static void main(String[] args) throws Exception {
String srcName = "red-tulips.jpg";
File srcFile = new File(srcName);
BufferedImage image = ImageIO.read(srcFile); System.out.println("Source image: " + srcName); BufferedImage blurredImage = blur(image); String dstName = "blurred-tulips.jpg";
File dstFile = new File(dstName);
ImageIO.write(blurredImage, "jpg", dstFile); System.out.println("Output image: " + dstName); } public static BufferedImage blur(BufferedImage srcImage) {
int w = srcImage.getWidth();
int h = srcImage.getHeight(); int[] src = srcImage.getRGB(0, 0, w, h, null, 0, w);
int[] dst = new int[src.length]; System.out.println("Array size is " + src.length);
System.out.println("Threshold is " + sThreshold); int processors = Runtime.getRuntime().availableProcessors();
System.out.println(Integer.toString(processors) + " processor"
+ (processors != 1 ? "s are " : " is ")
+ "available"); ForkBlur fb = new ForkBlur(src, 0, src.length, dst); ForkJoinPool pool = new ForkJoinPool(); long startTime = System.currentTimeMillis();
pool.invoke(fb);
long endTime = System.currentTimeMillis(); System.out.println("Image blur took " + (endTime - startTime) +
" milliseconds."); BufferedImage dstImage =
new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
dstImage.setRGB(0, 0, w, h, dst, 0, w); return dstImage;
}
}

  

标准实现
  除了利用分拆和合并框架实现对于任务的客户算法来执行多处理器的并发任务(例如在之前提到的ForkBlur.java实例)。
这里有些通用的有用的特性在java SE中已经由分拆和合并框架实现了。一个这种实现,在java se8 中介绍的通过分拆和合并事项的并发。在多处理器中并行排序对于大型数组比顺序排序快。然而,分拆和合并框架是如何实现的超过java教程。想获得更多信息,可以看java api文档。
  另外实现了分拆和合并框架的方法是在java.util.streams包中的方法,它是在Java SE 8 发布的Project Lambda调度的一部分中。想获得更多信息,可以参看Lambda Expressions节。

【翻译二十一】java-并发之分拆和合并的更多相关文章

  1. Java学习笔记二十一:Java面向对象的三大特性之继承

    Java面向对象的三大特性之继承 一:继承的概念: 继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类. 继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方 ...

  2. Java进阶(二十一)java 空字符串与null区别

    java 空字符串与null区别 1.类型 null表示的是一个对象的值,而并不是一个字符串.例如声明一个对象的引用,String a = null ; ""表示的是一个空字符串, ...

  3. 转:二十一、详细解析Java中抽象类和接口的区别

    转:二十一.详细解析Java中抽象类和接口的区别 http://blog.csdn.net/liujun13579/article/details/7737670 在Java语言中, abstract ...

  4. Java并发之synchronized关键字深度解析(二)

    前言 本文继续[Java并发之synchronized关键字深度解析(一)]一文而来,着重介绍synchronized几种锁的特性. 一.对象头结构及锁状态标识 synchronized关键字是如何实 ...

  5. 杭电oj 2098——分拆素数和(包含如何判断质数及优化),java实现

    question:分拆素数和 思路: 1.首先从1一直遍历到数据的1/2位置(因为后面的会和前面的重复),因为是要两个数,所以另一个数就是原数据减去遍历的数字(即i 和data-i),如果二者同时为质 ...

  6. JAVA之旅(二十一)——泛型的概述以及使用,泛型类,泛型方法,静态泛型方法,泛型接口,泛型限定,通配符

    JAVA之旅(二十一)--泛型的概述以及使用,泛型类,泛型方法,静态泛型方法,泛型接口,泛型限定,通配符 不知不觉JAVA之旅已经写到21篇了,不得不感叹当初自己坚持要重学一遍JAVA的信念,中途也算 ...

  7. Java并发之CyclicBarria的使用(二)

    Java并发之CyclicBarria的使用(二) 一.简介 之前借助于其他大神写过一篇关于CyclicBarria用法的博文,但是内心总是感觉丝丝的愧疚,因为笔者喜欢原创,而不喜欢去转载一些其他的文 ...

  8. Java设计模式(二十一):职责链模式

    职责链模式(Chain Of Responsibility Pattern) 职责链模式(Chain Of Responsibility Pattern):属于对象的行为模式.使多个对象都有机会处理请 ...

  9. Java笔记(二十一) 动态代理

    动态代理 一.静态代理 代理的背后一般至少有一个实际对象,代理的外部功能和实际对象一般是一样的, 用户与代理打交道,不直接接触实际对象.代理存在的价值: 1)节省成本比较高的实际对象创建开销,按需延迟 ...

随机推荐

  1. TP框架自动加载优先级

    $map = array('Think\Log'=>THINK_PATH.'Think\Log.php','Org\Util\Array'=>THINK_PATH.'Org\Util\Ar ...

  2. Delphi中Interface接口的使用方法

    示例注释(现在应该知道的): {   1.接口命名约定 I 起头, 就像类从 T 打头一样.   2.接口都是从 IInterface 继承而来; 若是从根接口继承, 可省略.   3.接口成员只能是 ...

  3. PyQt4多线程定时刷新控件

    1.通过事件关联和线程关联的方法刷新控件 self.listview=updatelistview()self.listview.updateText.connect(self.viewlist)   ...

  4. keystone v3 相关介绍

    1) 涉及到如下几个概念:User.Tenant.Role.Token.http://www.ibm.com/developerworks/cn/cloud/library/1506_yuwz_key ...

  5. c#.netGr idView1在div不局中

    <div style="margin:0 auto;text-align:center;" >//可以用GridView剧中 <asp:GridView ID=& ...

  6. WinForm开发框架--动态读取DLL模式

    1\ WinForm开发框架--动态读取DLL模式   http://www.2cto.com/kf/201306/217199.html 2\ 广州爱奇迪     http://www.iqidi. ...

  7. ios UITextView 计算文字内容大小

    先设置好 textView的内容文字,再调用以下代码,就能够得到文字内容的size,其中参数表示最大的size的尺寸,通常,高度应该不限制,宽度是控件的宽度. let newSize = statem ...

  8. ACM/ICPC 之 暴力打表(求解欧拉回路)-编码(POJ1780)

    ///找到一个数字序列包含所有n位数(连续)一次且仅一次 ///暴力打表 ///Time:141Ms Memory:2260K #include<iostream> #include< ...

  9. opencv-3.x.0-x86-mingw32-staticlib-gcc5.3.0-20160712.7z

    折腾了半天 用 cmake 3.5.0 + gcc5.3.0 编译 opencv3.x.0 静态库 opencv-3.0.0-x86-mingw32-staticlib-gcc5.3.0-201607 ...

  10. [转]安装和使用JD-Eclipse插件

    JD-Core 是一个免费的库,从一个或多个“.class”文件中 重构Java源代码.JD-Core可以用来恢复丢失的源代码,并深究Java运行时类库.支持Java 5的功能:如注释,泛型或键入“枚 ...