JDK8并行流及串行流区别原理详解

由于处理器核心的增长及较低的硬件成本允许低成本的集群系统,致使如今并行编程无处不在,并行编程似乎是下一个大事件。

Java 8 针对这一事实提供了新的 stream API 及简化了创建并行集合和数组的代码。让我们看一下它是怎么工作的。

假设 myList 是 List<Integer> 类型的,其中包含 500,000 个Integer值。在Java 8 之前的时代中,对这些整数求和的方法是使用 for 循环完成的。

for( int i : myList){
 result += i;
}

从 Java 8 开始,我们就可以使用stream完成同样的循环:

myList.stream().sum();

将此代码改为并行处理非常简单,仅需要使用 parallelStream() 代替 stream() 或 parallel()搭配stream使用:

mylist.stream().parallelStream().sum();

这样就可以成功的变为并行程序,所以将一个计算扩展到线程和CPU内核上并可用很容易就可以实现。但是我们都知道,多线程和并行处理的开销很大,所以重点是什么时候使用并行流,什么时候使用串行流才能获得更好的性能。

首先,让我们看看在幕后发生的事情。parallel stream 使用的是 Fork/Join 框架进行处理的,这意味着 stream 流的源会被拆分并移交给 fork/join 池中执行。

首先,我们找到了要考虑的第一点:并非所有的stream的源会像其它的stream的源一样可拆分。例如:ArrayList的内部实现是数组,由于可以通过计算出中间元素的索引来拆分,所以拆分这样的源会非常容易;假如使用LinkedList,则拆分数据会复杂的多:该实现必须遍历第一个条目中的所有元素,以便找到可以拆分的元素,所以LinkedList是并行流中性能差的例子。

这是我们可以保留的关于并行流性能的第一个事实:

S : 源集合必须可以有效拆分

拆分集合、管理 Fork/Join 任务、对象创建及 GC 也是算法上的开销,当且仅当在CPU核心上可简单完成或者集合足够大时,才值得这样做。

一个错误的例子:求5个整数的最大值。

Intstream.rangeClosed(1,5).reduce(Math::max).getAsInt();

系统为fork/join准备和处理数据的开销非常大,以至于串行流在此场景中要快得多。Math.max 方法在这里的CPU开销并不是很高,而且数据元素很少。

举个例子,在编写象棋游戏的时候,对每个棋子移动的评估。每一个评估都可以并行执行,并且我们有大量可能的下一步移动。这种情形非常适合并行处理。

这是我们可以保留的关于并行流性能的第二个事实:

N * Q: 因子”元素数量” * “ 每个元素的运行成本” 应该很大

但这同样意味着当每个元素的操作成本更高的时候,集合可以更小。或当每个元素的操作不那么占用大量CPU时,我们需要一个包含许多元素的非常大的集合,以便并行流的使用的到回报。

这直接取决于我们可以保留的第三个事实

C :CPU核心数量 - 越多越好 > 必须有1个

由于管理开销,在单核计算机上的并行流始终比串行流的性能差。

越多越好:实际上,这句话并不是在所有情况下都正确。例如:集合太小且CPU核心启动时处于节能模式进而导致CPU无事可做。

能否使用并行流,对每个元素的功能(function)也有要求,这涉及到并行流能否按照预期工作:

要求该功能(function):

  • 独立:每个元素的计算都不依赖或影响任何其他元素的计算
  • 无干扰:功能(function)执行的时候不会修改基础的数据源
  • 无状态

例:并行流中使用有状态lamdba方法的实例,来源自 Java JDK API

Set seen = Collection.synchronizedSet(new HashSet());
stream.parallel().map( e -> {
    if(seen.add(e))
      return 0;
    else
      return e;
  })...

于是,这是我们可以保留的第四个事实:

F :每个元素必须独立

总结:

还有其他情况不应该并行化流吗?有。

我们要始终考虑每一个元素的功能(function)在做什么及它是否适合运行在并行代码中。当方法是调用一些同步方法,并行流可能会在同步方法上等待,进而导致并行流的性能并没有想象中高。

同样的,在调用BI/O操作时,由于数据是按照顺序读取的,以I/O源作为流,也会发生同样的问题。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 在windows环境下安装jdk8、jdk9、jdk11、jdk12并自由切换

    在windows环境下安装jdk8.jdk9.jdk11.jdk12并自由切换在windows下安装jdk的各个版本安装各个版本的jdk,并设置安装路径配置环境各个版本的环境变量设置JAVA_HOME设置path切换jdk版本 在windows下安装jdk的各个版本安装各个版本的jdk,并设置安装路径 jdk8安装在C:\ProgramFile\Java jdk11安装在C:\ProgramFile\Java jdk12安装在C:\ProgramFile\Java jdk9安装在C:\Java9

  • Java8并行流中自定义线程池操作示例

    本文实例讲述了Java8并行流中自定义线程池操作.分享给大家供大家参考,具体如下: 1.概览 java8引入了流的概念,流是作为一种对数据执行大量操作的有效方式.并行流可以被包含于支持并发的环境中.这些流可以提高执行性能-以牺牲多线程的开销为代价 在这篇短文中,我们将看一下 Stream API的最大限制,同时看一下如何让并行流和线程池实例(ThreadPool instance)一起工作. 2.并行流Parallel Stream 我们先以一个简单的例子来开始-在任一个Collection类型

  • JAVA JDK8 List分组获取第一个元素的方法

    概述 在JAVA JDK8 List分组的实现和用法一文中介绍了JDK 8如何对list进行分组,但是没有提到如何在分组后,获取每个分组的第一个元素.其实这个也很简单,代码如下: package test; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import java.util.ArrayList; import java.util.List; imp

  • CentOS7上以rpm方式安装JDK8

    CentOS 7 安装成功后可能已经默认安装了OpenJDK的JRE,但平时进行JAVA开发时需要用完整的JDK,所以为了避免以后不必要的麻烦,在此卸载了OpenJDK的JRE,并重新安装了Oracle的JDK8 1.安装说明 系统环境:CentOS 7 安装方式:rpm 安装包:jdk-8u131-linux-x64.rpm 2.检查系统原安装版本 [hadoop@centos7-1 ~]$ java -version OpenJDK Version "1.8.0_131" Open

  • java8、jdk8日期转化成字符串详解

    java8.jdk8日期转化成字符串 新建日期工具类:DateUtils 新建方法:parseDate 实现方法parseDate public static String parseDate(LocalDate localDate,String pattern) { DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern); return localDate.format(dateTimeFormatt

  • JAVA JDK8 List分组的实现和用法

    概述 对List进行分组是日常开发中,经常遇到的,在JDK 8中对List按照某个属性分组的代码,超级简单. package test; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util

  • CentOS8 安装 jdk8 / java8的教程(推荐)

    前言 最开始我是想在CentOS8上使用 wget 下载,然后对它进行解压,配置环境变量,奈何搞了很长时间都没有搞好,于是放弃,使用yum直接安装. 1.安装方法 CentOS8上使用 yum 直接安装,环境变量自动配置好 2.查看是否已安装 看到下面结果,说明已经安装配置 jdk [root@localhost ~]# java -version openjdk version "1.8.0_222" OpenJDK Runtime Environment (build 1.8.0_

  • 详解Node.js串行化流程控制

    串行任务:需要一个接着一个坐的任务叫做串行任务. 可以使用回调的方式让几个异步任务按顺序执行,但如果任务过多,必须组织一下,否则过多的回调嵌套会把代码搞得很乱. 为了用串行化流程控制让几个异步任务按顺序执行,需要先把这些任务按预期的执行顺序放到一个数组中,这个数组将起到队列的作用:完成一个任务后按顺序从数组中取出下一个. 数组中的每个任务都是一个函数.任务完成后应该调用一个处理器函数,告诉它错误状态和结果. 为了演示如何实现串行化流程控制,我们准备做个小程序,让它从一个随机选择的RSS预定源中获

  • JDK8新特性之判空遍历写法

    在工作中,经常会处理各种变量,但往往会在使用变量的时候,要进行空判断,不然要报错. Java 8 提供了判空写法: Optional.ofNullable(变量).orElse(默认值): 例1:求字符串 s 的长度( 为空的时候返回0 ). 常规写法: String s = getKey(); if (s == null) { return 0; } else { return s.length(); } Java 8 写法: String s = getKey(); return Optio

  • JDK8并行流及串行流区别原理详解

    由于处理器核心的增长及较低的硬件成本允许低成本的集群系统,致使如今并行编程无处不在,并行编程似乎是下一个大事件. Java 8 针对这一事实提供了新的 stream API 及简化了创建并行集合和数组的代码.让我们看一下它是怎么工作的. 假设 myList 是 List<Integer> 类型的,其中包含 500,000 个Integer值.在Java 8 之前的时代中,对这些整数求和的方法是使用 for 循环完成的. for( int i : myList){ result += i; }

  • PHP常量及变量区别原理详解

    常量: 用于储存一个不会变化也不希望变化的数据的标示符(命名规则与变量相同) 定义形式: 使用 define() 函数定义 使用形式:define("常量名" ,常量值) 使用 counst 语法定义 使用形式:counst 常量名 = 常量值 使用常量:有两种形式1,直接使用名字 2,使用constant()函数:constant("常量名") 常量与变量的区别: 定义形式不同: 使用形式不同(常量不需要 $ 符号): 可变程度不同(常量的值不可改变或者销毁):

  • C++ 中cerr和cout的区别实例详解

    C++ 中cerr和cout的区别实例详解 前言: cerrThe object controls unbuffered insertions to the standard error output as a byte stream. Once the object is nstructed, the expression cerr.flags & unitbuf is nonzero. Example // iostream_cerr.cpp // compile with: /EHsc /

  • MySQL 设计和命令行模式下建立详解

    MySQL 设计和命令行模式下建立详解 系列文章: MySQL 设计和命令行模式下建立详解 C++利用MySQL API连接和操作数据库实例详解 1.数据表的设计 MySQL数据库管理系统(DBMS)中,包含的MySQL中定义数据字段的类型对你数据库的优化是非常重要的.MySQL支持多种类型,大致可以分为三类:数值.日期/时间和字符串(字符)类型. 下面以大学熟悉的学生选课管理系统中用到的数据库为例,来设计相应的数据表.主要有三张表:学生表,课程表和选课表. 学生表设计: 字段(Field) 类

  • php array_walk array_map array_filter区别案例详解

    php 开发经常遇到数组处理,会涉及到题目中这几个函数.这个函数功能类似,很多时候容易混淆. array_walk: array_walk - 使用用户自定义函数对数组中的每个元素做回调处理 1. 用户自定义的函数处理每一个元素 2. 直接修改原数组,不会创建新的数组 3. 可以传递额外的参数 Example #1 : <?php $fruits = array("d" => "lemon", "a" => "oran

  • Python中read,readline和readlines的区别案例详解

    python中有神奇的三种读操作:read.readline和readlines read()  : 一次性读取整个文件内容.推荐使用read(size)方法,size越大运行时间越长 readline()  :每次读取一行内容.内存不够时使用,一般不太用 readlines()   :一次性读取整个文件内容,并按行返回到list,方便我们遍历 一般小文件我们都采用read(),不确定大小你就定个size,大文件就用readlines() 1)我们先用read来完整读取一个小文件,代码如下: f

  • Java hashCode原理以及与equals()区别联系详解

    目录 1.什么是hashCode 2.equals()与hashCode()的联系 3.为什么重写equals()的同时要重写hashCode()方法 3.1.测试一 3.2.测试二 4.由hashCode()造成的内存泄露问题 5.基本数据类型和String类型的hashCode()方法和equals()方法 1.什么是hashCode hashCode就是对象的散列码,是根据对象的某些信息推导出的一个整数值,默认情况下表示是对象的存储地址.通过散列码,可以提高检索的效率,主要用于在散列存储结

  • C++ 中引用与指针的区别实例详解

    C++ 中引用与指针的区别实例详解 引用是从C++才引入的,在C中不存在.为了搞清楚引用的概念,得先搞明白变量的定义及引用与变量的区别,变量的要素一共有两个:名称与空间. 引用不是变量,它仅仅是变量的别名,没有自己独立的空间,它只符合变量的"名称"这个要素,而"空间"这个要素并不满足.换句话说,引用需要与它所引用的变量共享同一个内存空间,对引用所做的改变实际上是对所引用的变量做出修改.并且引用在定义的时候就必须被初始化.     参数传递的类型及相关要点: 1 按值

  • jQuery中的on与bind绑定事件区别实例详解

    on(events,[selector],[data],fn) events:一个或多个用空格分隔的事件类型和可选的命名空间,如"click"或"keydown.myPlugin" . selector:一个选择器字符串用于过滤器的触发事件的选择器元素的后代. data:当一个事件被触发时要传递event.data给事件处理函数. fn:该事件被触发时执行的函数. false 值也可以做一个函数的简写,返回false. bind(type,[data],fn) 为每

  • jQuery中text() val()和html()的区别实例详解

    简单的说:html()和text()的区别主要在于是否包含标签.而val()针对的是表单元素. 但是有时还是不是那么太清晰. html(),val(),text()都分为有参和无参. 举例说明它们的不同之处: html()在没有参数的情况下,取得第一个匹配元素的内容.必须要注意的是,即使匹配多个,也只能取得匹配的第一个元素. 如: <body> <p>你选中这段文字后,看看它们的文本颜色和背景色,就能明白::selection的作用.</p> <h3>选中下

随机推荐