Java Fork/Join框架

 更新时间:2016年09月11日 15:31:15   作者:robin  
Fork/Join框架是Java7中新增的一项特性,也是Java7平台的其中一项主要改进。下面我们就来简单探讨下Java的Fork/Join框架

Fork/Join框架是ExecutorService接口的一个实现,通过它我们可以实现多进程。Fork/Join可以用来将一个大任务递归的拆分为多个小任务,目标是充分利用所有的资源尽可能增强应用的性能。

和任何ExecutorService接口的实现一样,Fork/Join也会使用线程池来分布式的管理工作线程。Fork/Join框架的独特之处在于它使用了work-stealing(工作窃取)算法。通过这个算法,工作线程在无事可做时可以窃取其它正在繁忙的线程的任务来执行。

Fork/Join框架的核心是ForkJoinPool类,一个AbstractExecutorService类的子类。ForkJoinPool实现了核心的work-stealing算法并可以执行ForkJoinTask处理。

基础用法

使用Fork/Join框架的第一步是编写执行碎片任务的代码。要编写的代码类似如下伪代码:

if 任务足够小:
 直接执行任务
else:
 将任务切成两个小任务
 执行两个小任务并等待结果

使用ForkJoinTask子类来封装如上的代码,通常会使用一些JDK提供的类,使用的有RecursiveTask(这个类会返回一个结果)和RecursiveAction两个类。

在准备好ForkJoinTask子类后,创建一个代表所有任务的对象,并将之传递给一个ForkJoinPool实例的invoke()方法。

由模糊到清晰

为了辅助理解Fork/Join框架是如何工作的,我们使用一个案例来进行说明:比如对一张图片进行模糊处理。我们用一个整型数组表示图片,其中的每个数值代表一个像素的颜色。被模糊的图片也用一个同等长度的数组来表示。

执行模糊是通过对代表图片的每个像素进行处理实现的。计算每个像素与其周围像素的均值(红黄蓝三原色的均值),计算生成的结果数组就是模糊后的图片。由于代表图像的通常都是一个大数组,整个处理过程需要通常会需要很多时间。可以使用Fork/Join框架利用多处理器系统上的并发处理优势来进行提速。下面是一个可能的实现:

package com.zhyea.robin;
 
import java.util.concurrent.RecursiveAction;
 
public class ForkBlur extends RecursiveAction {
 
  private int[] mSource;
  private int mStart;
  private int mLength;
  private int[] mDestination;
 
  // 处理窗口大小; 需要是一个奇数.
  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++) {
      // 计算平均值.
      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;
      }
 
      // 重组目标像素.
      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. 创建一个代表要执行的任务的对象:

// src 表示源图片像素的数组
// dst 表示生成的图片的像素
ForkBlur fb = new ForkBlur(src, 0, src.length, dst);

2. 创建一个运行任务的ForkJoinPool实例:

ForkJoinPool pool = new ForkJoinPool();

3. 运行任务:

pool.invoke(fb);

在源代码中还包含了一些创建目标图片的代码。具体参考ForkBlur示例。

标准实现

要使用Fork/Join框架按自定义的算法在多核系统上执行并发任务当然需要实现自定义的类了(比如之前我们实现的ForkBlur类)。除此之外,在JavaSE中已经在广泛使用Fork/Join框架的一些特性了。比如Java8中的java.util.Arrays类的parallelSort()方法就使用了Fork/Join框架。具体可以参考Java API文档。

Fork/Join框架的另一个实现在java.util.streams包下,这也是java8的Lambda特性的一部分。

相关文章

  • Java的Spring框架的三种连接池的基本用法示例

    Java的Spring框架的三种连接池的基本用法示例

    这篇文章主要介绍了Java的Spring框架的三种连接池的基本用法示例,Spring框架是Java下注明的SSH三大web开发框架之一,需要的朋友可以参考下
    2015-11-11
  • 关于集合和字符串的互转实现方法

    关于集合和字符串的互转实现方法

    下面小编就为大家带来一篇关于集合和字符串的互转实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-08-08
  • 利用Java生成带有文字的二维码

    利用Java生成带有文字的二维码

    二维码在我们现在的生活中可谓是随处可见,这篇文章主要是介绍如何利用Java生成带有文字的二维码,对大家学习Java具有一定的参考借鉴价值。有需要的朋友们下面来一起看看吧。
    2016-09-09
  • Javaweb接收表单数据并处理中文乱码

    Javaweb接收表单数据并处理中文乱码

    这篇文章主要介绍了Javaweb接收表单数据并处理中文乱码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-07-07
  • 简介Java程序的Shell脚本包装

    简介Java程序的Shell脚本包装

    这篇文章主要介绍了简介Java程序的Shell脚本包装,将Java运用于脚本程序当中,有时或许是个不错的主意~需要的朋友可以参考下
    2015-07-07
  • java中File类应用遍历文件夹下所有文件

    java中File类应用遍历文件夹下所有文件

    这篇文章主要为大家详细介绍了java中File类应用遍历文件夹下所有文件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08
  • Java中stream处理中map与flatMap的比较和使用案例

    Java中stream处理中map与flatMap的比较和使用案例

    这篇文章主要介绍了Java中stream处理中map与flatMap的比较和使用案例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • SpringBoot + Spring Security 基本使用及个性化登录配置详解

    SpringBoot + Spring Security 基本使用及个性化登录配置详解

    这篇文章主要介绍了SpringBoot + Spring Security 基本使用及个性化登录配置详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • HTTP中get和post的区别详解

    HTTP中get和post的区别详解

    这篇文章主要为大家详细介绍了HTTP中get和post的区别,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08
  • Java面向对象之成员隐藏与属性封装操作示例

    Java面向对象之成员隐藏与属性封装操作示例

    这篇文章主要介绍了Java面向对象之成员隐藏与属性封装操作,结合实例形式分析了Java面向对象程序设计中成员的隐藏及属性封装相关实现与使用操作技巧,需要的朋友可以参考下
    2018-06-06

最新评论