JSE 8 的范例代码:fork / join
fork / join框架是ExecutorService
接口的实现,可帮助您利用多个处理器。它专为可以递归分解成小块的工作而设计。目标是使用所有可用的处理能力来增强应用程序的性能。
与任何ExecutorService
实现一样,fork / join框架将任务分配给线程池中的工作线程。fork / join框架是不同的,因为它使用了工作窃取算法。不用做的事情的工作线程可以从仍然忙碌的其他线程中窃取任务。
ork / join框架的中心是ForkJoinPool
类,是 类的扩展AbstractExecutorService
。ForkJoinPool
实现核心工作窃取算法并可以执行 ForkJoinTask
进程。
基本用法
使用fork / join框架的第一步是编写执行一部分工作的代码。您的代码应类似于以下伪代码:
如果(我的工作部分足够小)
直接做这项工作
其他
把我的工作分成两部分
调用这两个部分并等待结果
将此代码包装在ForkJoinTask
子类中,通常使用其中一种更专业的类型 RecursiveTask
(可以返回结果)或 RecursiveAction
。
在您的ForkJoinTask
子类是准备好了,创建一个表示要完成所有的工作对象,把它传递给invoke()
一个方法ForkJoinPool
实例。
模糊为Clarity
为了帮助您了解fork / join框架的工作原理,请考虑以下示例。假设您想模糊图像。原始源图像由整数数组表示,其中每个整数包含单个像素的颜色值。模糊的目标图像也由与源相同大小的整数数组表示。
通过一次一个像素地处理源阵列来完成模糊。每个像素与其周围像素平均(红色,绿色和蓝色分量被平均),结果放在目标数组中。由于图像是大型数组,因此此过程可能需要很长时间。通过使用fork / join框架实现算法,您可以利用多处理器系统上的并发处理。
现在实现抽象compute()
方法,该方法直接执行模糊或将其拆分为两个较小的任务。简单的数组长度阈值有助于确定是执行还是拆分工作。
如果以前的方法在类的子RecursiveAction
类中,那么将任务设置为在a中运行ForkJoinPool
很简单,并涉及以下步骤:
-
创建一个代表要完成的所有工作的任务。
//源图像像素在src中 //目标图像像素在dst中 ForkBlur fb = new ForkBlur(src,0,src.length,dst);
-
创建
ForkJoinPool
将运行任务的那个。ForkJoinPool pool = new ForkJoinPool();
-
运行任务。
pool.invoke(FB)
package demo_08279.callable;
/**
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle or the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
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;
}
}