java8提供一个fork/join framework,fork/join框架是ExecutorService接口的一个实现,它可以帮助你充分利用你电脑中的多核处理器,它的设计理念是将一个任务分割成多个可以递归执行的小任务,这些小任务可由不同的处理器进行并行执行,显然这样设计的一个目的就是提高应用的性能。

fork/join框架的和兴是ForkJoinPool类,是AbstractExecutorService类的扩展,它继承了核心的并行执行算法,可以执行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子类进行封装,通常继承ForkJoinTask更具体的子类来实现,如RecursiveTask或者RecursiveAction,当子类创建完毕,再创建一个表示要处理的任务的对象,将它作为一个参数传递给ForkJoinPool实例的invoke()方法。官方文档给了一个对照片进行模糊处理的示例,以这个示例为切入点了解fork/join操作,在对照片进行模糊处理时,思路是将一个图片转成一个数组,这个数组中的每个值代表一个像素,然后对这个像素进行处理,显然将图片转成的数组将会是一个很大的数组,为了加快处理,可以对数组进行切割,切割成一个个小的数组,然后对这个小的数组进行并行处理,将处理后的结果再合并成一个大的结果返给用户,代码如下:

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;
} // 计算原图片的平均像素, 将结果写道目的数组中.
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;
}
}
//定义计算的阈值
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 = "C:\\Users\\Administrator\\Desktop\\信息文件\\研究院工作\\个人信息\\aaa.jpg";
File srcFile = new File(srcName);
BufferedImage image = ImageIO.read(srcFile); System.out.println("Source image: " + srcName); BufferedImage blurredImage = blur(image); String dstName = "C:\\Users\\Administrator\\Desktop\\信息文件\\研究院工作\\个人信息\\bbb.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任务继承RecursiveAction抽象类,它的执行过程整体分为三步:

1、创建一个表示需要执行动作的任务:ForkBlur fb=new ForkBlur(...)

2、创建ForkJoinPool对象,以触发任务的执行:ForkJoinPool pool=new ForkJoinPool();

3、执行任务 pool.invoke(fb)

并行计算在流中也有很多引用,它的思想是:java会将流分割成多分子流,聚合操作会以并行的形式遍历处理这些子流,最后将结果汇合成一个结果,在创建平行流的时候需要调用paralleStream()方法指明创建并行流:

double average = roster

    .parallelStream()
.filter(p -> p.getGender() == Person.Sex.MALE)
.mapToInt(Person::getAge)
.average()
.getAsDouble();

java中的并发归约操作,前面讲到,如果将人按照性别的方式进行区分,则归约操作如下:

Map<Person.Sex, List<Person>> byGender =
roster
.stream()
.collect(
Collectors.groupingBy(Person::getGender)); 

上面的代码的结果和下面的操作方式是等价的:

ConcurrentMap<Person.Sex, List<Person>> byGender =

    roster
.parallelStream()
.collect(
Collectors.groupingByConcurrent(Person::getGender)); 

上面的操作方式称为并发归约操作(concurrent reduction),如果以下条件均满足,则一个包含collect操作的管道就是并发归约操作:

1、流是并行的,stream is parallel

2、collect操作的参数,也就是待操作的集合需要有Collector.Characteristics.CONCURRENT特征,查看一个集合的特征,可以调用Collector.characteristics方法

3、无论流是无序的,还是集合具有Collector.Characteristics.UNORDERED的特征,为了保证流是无序的,可以调用BaseStream.unordered操作。

一个管道在处理流的顺序取决于这个流值顺序执行的还是并行执行的(in serial or in parallel)、流的源和中间操作;例如打印一个list集合中的元素,这里使用forEach操作,代码如下: 

Integer[] intArray = {1, 2, 3, 4, 5, 6, 7, 8 };
List<Integer> listOfIntegers =
new ArrayList<>(Arrays.asList(intArray)); System.out.println("listOfIntegers:");
listOfIntegers
.stream()
.forEach(e -> System.out.print(e + " "));
System.out.println(""); System.out.println("listOfIntegers sorted in reverse order:");
Comparator<Integer> normal = Integer::compare;
Comparator<Integer> reversed = normal.reversed();
Collections.sort(listOfIntegers, reversed);
listOfIntegers
.stream()
.forEach(e -> System.out.print(e + " "));
System.out.println(""); System.out.println("Parallel stream");
listOfIntegers
.parallelStream()
.forEach(e -> System.out.print(e + " "));
System.out.println(""); System.out.println("Another parallel stream:");
listOfIntegers
.parallelStream()
.forEach(e -> System.out.print(e + " "));
System.out.println(""); System.out.println("With forEachOrdered:");
listOfIntegers
.parallelStream()
.forEachOrdered(e -> System.out.print(e + " "));
System.out.println(""); ===========执行结果为============= listOfIntegers:
1 2 3 4 5 6 7 8
listOfIntegers sorted in reverse order:
8 7 6 5 4 3 2 1
Parallel stream:
3 4 1 6 2 5 7 8
Another parallel stream:
6 3 1 5 7 8 4 2
With forEachOrdered:
8 7 6 5 4 3 2 1

上面的代码中,第三和第四个管道的输出结果显然是无序的,而第五个管道使用forEachOrdered方法,这时候无论使用的stream还是parallelStream,都使得执行过程按照执行顺序进行,当然,这也就是失去了并行流的优势了。

以上是java并行处理数据的小示例,判断什么时候使用并行处理也是一个问题,知识是无涯的,不要小看任何东西...

java8-并行计算的更多相关文章

  1. Java8 Lambda表达式教程

    转自:http://blog.csdn.net/ioriogami/article/details/12782141 1. 什么是λ表达式 λ表达式本质上是一个匿名方法.让我们来看下面这个例子: pu ...

  2. Java8部分新特性的学习

    Java8中的新特性 一.Lambda表达式 Lambda表达式可以理解为一种可传递的匿名函数:它没有名称,但又参数列表.函数主体.返回类型,可能还有一个可以抛出的异常列表. 匿名:和匿名类类似的,它 ...

  3. 【读书笔记】《写给大忙人看的Java SE 8》——Java8新特性总结

    虽然看过一些Java 8新特性的资料,但是平时很少用到,时间长了就忘了,正好借着Java 9的发布,来总结下一些Java 8中的新特性. 接口中的默认方法和静态方法 先考虑一个问题,如何向Java中的 ...

  4. Java8一:Lambda表达式教程

    1. 什么是λ表达式 λ表达式本质上是一个匿名方法.让我们来看下面这个例子: public int add(int x, int y) {         return x + y;     } 转成 ...

  5. java8 Stream的实现原理 (从零开始实现一个stream流)

    1.Stream 流的介绍 1.1 java8 stream介绍 java8新增了stream流的特性,能够让用户以函数式的方式.更为简单的操纵集合等数据结构,并实现了用户无感知的并行计算. 1.2  ...

  6. java8新特性--Stream的基本介绍和使用

    什么是Stream? Stream是一个来自数据源的元素队列并可以进行聚合操作. 数据源:流的来源. 可以是集合,数组,I/O channel, 产生器generator 等 聚合操作:类似SQL语句 ...

  7. Java8 使用

    Java8 使用 链接:https://www.jianshu.com/p/936d97ba0362 链接:https://www.jianshu.com/p/41de7b5ac7b9 本文主要总结了 ...

  8. 【java多线程】java8的流操作api和fork/join框架

    原文:https://blog.csdn.net/u011001723/article/details/52794455/ 一.测试一个案例,说明java8的流操作是并行操作 1.代码 package ...

  9. Java8函数之旅 (一) 开始认识lambda

    系列之前我想说的   最近有一段时间没写博客了,这几天回到学校,才闲下来,决定写一写最近学习到的知识,既是为了分享,也是为了巩固.之前看到过一篇调查,调查说的是学习新知识,光只是看的话,知识的获取率只 ...

  10. 《写给大忙人看的Java SE 8》——Java8新特性总结

    阅读目录 接口中的默认方法和静态方法 函数式接口和Lambda表达式 Stream API 新的日期和时间 API 杂项改进 参考资料 回到顶部 接口中的默认方法和静态方法 先考虑一个问题,如何向Ja ...

随机推荐

  1. 服务器重启后oracle监听无法打开

    我重启服务器后不知道为啥监听启动不了,试过各种办法都不行,然后把监听删了重新配置就可以了

  2. 博弈论[leetocde913]

    class Solution { static final int MOUSE_WIN = 1; static final int CAT_WIN = 2; static final int DRAW ...

  3. element的el-table合计显示不出来

    在updated中写入 this.$nextTick(() => { this.$refs["printStatisticsTableData"].doLayout(); } ...

  4. 安装robotframwork 报错Requirement already satisfied

    pip install 的时候报错信息为,在这里插入图片描述对于这样的问题需要指定安装路径pip install --target=d:\python\python37\lib\site-packag ...

  5. python机器学习——逻辑回归方法

    背景与原理: 线性回归可以实现对连续结果的预测,但是现实生活中我们常见的另一种问题是分类问题,尤其是二分类问题,在这种情况下使用线性回归就不太合适了,我们实际上需要计算出的是一个在$[0,1]$之间的 ...

  6. Day09-方法

    方法 一.何谓方法 java方法是语句的集合,他们在一起执行一个功能 方法是解决一类问题的步骤的有序集合 方法包含于类或对象中 方法在程序中被创建,在其他地方被引用 设计方法的原则: 方法的本意是功能 ...

  7. 【python基础笔记-3】decimal模块解决浮点数计算精度问题

    通过Decimal('123.456')对象实例化后做 + - * / 等运算符操作计算结果不会出现精度问题. Tips:值得注意的2点是 1.Decimal接收的入参是str,所以如果原本操作的数据 ...

  8. python笔记:list--pop与remove的区别

    正常情况下: # coding=utf-8 fruit = ['apple', 'pear', 'banana' ] #指定索引删除 fruit.pop(0) #符合元素删除,具体数值 fruit.r ...

  9. excel的几个常用方法

    --笔记开始: 1.if(条件,真值,假值),类似于编程语言中的三元运算符.条件为真时返回真值,条件为假时返回假值. 2.match(目标值,查找区域,查找类型),一般查找类型为0(等值查找),查找区 ...

  10. 20200923--计算鞍点(奥赛一本通P91 4)

    给定一个5*5的矩阵,每行只有一个最大值,每列只有一个最小值,寻找这个矩阵的鞍点.鞍点指的是矩阵中的一个元素,它是所在行的最大值,并且是所在列的最小值. 例如:在下面的例子中(第4行第1列的元素就是鞍 ...