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对象占用字节数的方法

    计算一个Java对象占用字节数的方法

    这篇文章主要介绍了计算一个Java对象占用字节数的方法,较为详细的分析了Java中各类对象所占用的字节数,需要的朋友可以参考下
    2015-01-01
  • 在Java中判断两个Long对象是否相等

    在Java中判断两个Long对象是否相等

    这篇文章主要介绍了在Java中判断两个Long对象是否相等的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Java8新特性之JavaFX 8_动力节点Java学院整理

    Java8新特性之JavaFX 8_动力节点Java学院整理

    这篇文章主要介绍了Java8新特性之JavaFX 8的相关知识,非常不错,具有参考借鉴价值,需要的朋友参考下吧
    2017-06-06
  • MyBatis根据条件批量修改字段的方式

    MyBatis根据条件批量修改字段的方式

    这篇文章主要介绍了MyBatis根据条件批量修改字段的方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • Java中的可变参数常见用法实例总结

    Java中的可变参数常见用法实例总结

    这篇文章主要介绍了Java中的可变参数常见用法,结合实例形式总结分析了java可变参数的常见功能、使用方法及相关操作注意事项,需要的朋友可以参考下
    2019-10-10
  • Zookeeper全局唯一ID生成方案解析

    Zookeeper全局唯一ID生成方案解析

    这篇文章主要介绍了Zookeeper全局唯一ID生成方案解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-12-12
  • Feign+mybatisplus搭建项目遇到的坑及解决

    Feign+mybatisplus搭建项目遇到的坑及解决

    这篇文章主要介绍了Feign+mybatisplus搭建项目遇到的坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • 通过Java创建Socket连接到服务器方式

    通过Java创建Socket连接到服务器方式

    这篇文章主要介绍了通过Java创建Socket连接到服务器方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • java课程设计之坦克大战

    java课程设计之坦克大战

    这篇文章主要为大家详细介绍了java课程设计之坦克大战,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-12-12
  • 浅谈java中字符串数组、字符串、整形之间的转换

    浅谈java中字符串数组、字符串、整形之间的转换

    这篇文章主要介绍了浅谈java中字符串数组、字符串、整形之间的转换,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11

最新评论