最近在使用Java8的并行流时遇到了坑,线上排查问题时花了较多时间,分享出来与大家一起学习与自查

// 此处为坑
List<Java8Demo> copy = Lists.newArrayList();
numbers.parallelStream().forEach(item -> {
copy.add(new Java8Demo(item));
});

上图用到了parallelStrem并行流,在循环内部往共享变量copy内写值,由于ArrayList本身不具备线程安全性,导致得到的copy内容有缺失。

总结经验如下:

  1. 在并行流内部不能对外部共享变量做写操作;
  2. 如有需要,使用收集器实现上述并行流,收集器在内部即使使用ArrayList,也不会造成问题!

提供两种解决方案:

  • 串行

    // stream串行
    List<Java8Demo> copy = Lists.newArrayList();
    numbers.stream().forEach(item -> {
    copy.add(new Java8Demo(item));
    });
  • 收集器
    // 并行使用收集器
    List<Java8Demo> copy = numbers.parallelStream().map(Java8Demo::new).collect(Collectors.toList());

可运行Demo.java

package acc.biz.impl;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors; import com.google.common.collect.Lists; public class Demo { private Integer value; public Demo(Integer value) {
this.value = value;
} public static List<Integer> numbers = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); public static void main(String[] args) {
/** parallelStream并行 */
int count1 = 1;
while (count1 < 100) {
// 此处为坑
List<Demo> copy = Lists.newArrayList();
numbers.parallelStream().forEach(item -> {
copy.add(new Demo(item));
}); // 打印错误
if (copy.size() != numbers.size()) {
System.out.println(
new StringBuilder().append("parallelStream循环第").append(count1).append("次报错,numbers.size: [")
.append(numbers.size()).append("],copy.size: [").append(copy.size()).append("]"));
break;
} count1++;
} /** stream串行 */
int count2 = 1;
while (count2 < 100) {
// stream串行
List<Demo> copy = Lists.newArrayList();
numbers.stream().forEach(item -> {
copy.add(new Demo(item));
}); // 打印错误
if (copy.size() != numbers.size()) {
System.out.println(new StringBuilder().append("stream循环第").append(count2).append("次报错,numbers.size: [")
.append(numbers.size()).append("],copy.size: [").append(copy.size()).append("]"));
break;
} count2++;
} /** Collectors并行 */
int count3 = 1;
while (count3 < 100) {
// 并行使用收集器
List<Demo> copy = numbers.parallelStream().map(Demo::new).collect(Collectors.toList()); // 打印错误
if (copy.size() != numbers.size()) {
System.out.println(
new StringBuilder().append("Collectors循环第").append(count3).append("次报错,numbers.size: [")
.append(numbers.size()).append("],copy.size: [").append(copy.size()).append("]"));
break;
} count3++;
}
} public Integer getValue() {
return value;
} public void setValue(Integer value) {
this.value = value;
}
}

分享一个知识点:

Java的一个新功能静态方法指定泛型类型。

Java8 的静态泛型方法是: YourClass.<T>yourMethod();

C# 的静态泛型方法是: YourClass<T>.yourMethod();

如下的例子:

package demo;

import j.m.XList;
import j.m.XMap; public class Main { public static void main(String[] args) {
//public static final <U> XList<U> fromJSON(String json); 这是j.jar中的方法原型,
double s = XList.<XMap<String, Object>>fromJSON("[{id:1,name:'aaa',money:1000.00},{id:2,name:'bbb',money:10000.00},{id:3,name:'ccc',money:30000.00}]")
.parallelStream()
.map(x -> x.getDouble("money"))//不指定的话这里的x类型是Object
.reduce(0.0, Double::sum);//用到了并行流,不可用累加到外部变量的方式求和
System.out.println(s);//
}
}

在使用Java8并行流时的问题分析的更多相关文章

  1. Java8并行流使用注意事项

    对于从事Java开发的童鞋来说,相信对于Java8的并行流并不陌生,没错,我们常常用它来执行并行任务,但是由于并行流(parallel stream)采用的是享线程池,可能会对我们的性能造成严重影响, ...

  2. Java8新特性 并行流与串行流 Fork Join

    并行流就是把一个内容分成多个数据块,并用不同的线程分 别处理每个数据块的流. Java 8 中将并行进行了优化,我们可以很容易的对数据进行并 行操作. Stream API 可以声明性地通过 para ...

  3. java8新特性——并行流与顺序流

    在我们开发过程中,我们都知道想要提高程序效率,我们可以启用多线程去并行处理,而java8中对数据处理也提供了它得并行方法,今天就来简单学习一下java8中得并行流与顺序流. 并行流就是把一个内容分成多 ...

  4. Java8新特性 - 并行流与串行流

    并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流. Java8中将并行进行了优化,我们可以很容易的对数据进行并行操作.Stream API可以声明性地通过parallel()和 ...

  5. Java8使用并行流(ParallelStream)注意事项

    Java8并行流ParallelStream和Stream的区别就是支持并行执行,提高程序运行效率.但是如果使用不当可能会发生线程安全的问题.Demo如下: public static void co ...

  6. 三、并行流与串行流 Fork/Join框架

    一.并行流概念: 并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流. java8中将并行进行了优化,我们可以很容易的对数据进行并行操作.Stream API可以声明性的通过pa ...

  7. Tomcat 应用中并行流带来的类加载问题

    本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/f-X3n9cvDyU5f5NYH6mhxQ作者:肖铭轩.王道环 随着 Java8 的不断流行, ...

  8. 【转】Java8 Stream 流详解

      当我第一次阅读 Java8 中的 Stream API 时,说实话,我非常困惑,因为它的名字听起来与 Java I0 框架中的 InputStream 和 OutputStream 非常类似.但是 ...

  9. java8学习之收集器枚举特性深度解析与并行流原理

    首先先来找出上一次[http://www.cnblogs.com/webor2006/p/8353314.html]在最后举的那个并行流报错的问题,如下: 在来查找出上面异常的原因之前,当然得要一点点 ...

随机推荐

  1. Codeforce 296A - Yaroslav and Permutations

    Yaroslav has an array that consists of n integers. In one second Yaroslav can swap two neighboring a ...

  2. html5的理解

    1.良好的移动性,以移动设备为主 2.响应式设计,以适应自动变化的屏幕尺寸 3.支持离线缓存技术,webStorage本地缓存 4.新增canvas.video.audio等新标签元素,新增特殊内容元 ...

  3. dubbo-启动时检查

    dubbo缺醒会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止Spring初始化完成,以便上线时,能及早发现问题,默认 check=true 可以通过 check=false 关闭检查,比如 ...

  4. UI自动化(一)html基础

    前端的三把利器 HTML:赤裸的一个人 CSS:华丽的衣服 JS/JavaScript:赋予这个人的行为,也就是动起来 DOM 就是将页面变成可操 HTML(超文本标记语言) html代码实际上就是一 ...

  5. qtquickcontrols2控件集(使用参考重构)

           随着Qt的版本升级,其自带的controls控件库也不断升级,目前已经到了2.3的版本.本文通过重构并且解读Qt自带的gallery例程,说明新版本controls控件库的相关特性 来自 ...

  6. 启动Activiti项目报错:org.activiti.engine.ActivitiObjectNotFoundException: no deployed process definition found with id '22501'

    背景 启动activiti项目时,出现错误org.activiti.engine.ActivitiObjectNotFoundException: no deployed process defini ...

  7. Flask学习【第3篇】:蓝图、基于DBUtils实现数据库连接池、上下文管理等

    小知识 子类继承父类的三种方式 class Dog(Animal): #子类 派生类 def __init__(self,name,breed, life_value,aggr): # Animal. ...

  8. 没有使用Material组件

    // 这个App没有使用Material组件, 如Scaffold. // 一般来说, app没有使用Scaffold的话,会有一个黑色的背景和一个默认为黑色的文本颜色. // 这个app,将背景色改 ...

  9. 集训总结DAY.1(18.5.22)——KMP

    DAY 1——5.22 in the morning 依稀记得我们有一场contest. at night chf大佬讲KMP,先膜一波~~~ luoguP3375KMP模板题 KMP算法,又称模式匹 ...

  10. windows下使用LibreOffice的体验

    我在家里一般是使用Ubuntu,学校这个网络需要e信拨号,还是只能用Windows主用,以前我在Ubuntu一直使用的是LibreOffice,这次看见学校电脑很乱了就重装了系统,MSOffice真的 ...