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

摘要:
Java8并行流ParallelStream和Stream的区别就是支持并行执行,提高程序运行效率。但是如果使用不当可能会发生线程安全的问题。Demo如下:publicstaticvoidconcurrentFun(){ListlistOfIntegers=newArrayList();for{listOfIntegers.add;}ListparallelStorage=newArrayList();listOfIntegers.parallelStream().filter.forEach;System.out.println();parallelStorage.stream().forEachOrdered;System.out.println();System.out.println;try{TimeUnit.SECONDS.sleep;}catch{e.printStackTrace();}parallelStorage.stream().forEachOrdered;}程序运行结果如下:null7256586074343668705428305052261644121448224640244218203868100null4826684867880766264909294889698Sleep5secnull7256586074343668705428305052261644121448224640244218203868100null4826684867880766264909294889698除了以上在ForEach里面添加集合元素会出现这种问题,以下这种方式也会:listOfIntegers.parallelStream().map.forEachOrdered;两个问题:1.为什么parallelStorage的大小不固定?所以,在采用并行流收集元素到集合中时,最好调用collect方法,一定不要采用Foreach方法或者map方法。

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

public static void concurrentFun() {
        List<Integer> listOfIntegers =
                new ArrayList<>();
        for (int i = 0; i <100; i++) {
            listOfIntegers.add(i);
        }
        List<Integer> parallelStorage = new ArrayList<>() ;
        listOfIntegers
                .parallelStream()
                .filter(i->i%2==0)
                .forEach(i->parallelStorage.add(i));
        System.out.println();

        parallelStorage
                .stream()
                .forEachOrdered(e -> System.out.print(e + " "));

        System.out.println();
        System.out.println("Sleep 5 sec");
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        parallelStorage
                .stream()
                .forEachOrdered(e -> System.out.print(e + " "));
    }

程序运行结果如下:

null 72 56 58 60 74 34 36 68 70 54 28 30 50 52 26 16 44 12 14 48 22 46 40 24 42 18 20 38 6 8 10 0 null 4 82 66 84 86 78 80 76 62 64 90 92 94 88 96 98 
Sleep 5 sec
null 72 56 58 60 74 34 36 68 70 54 28 30 50 52 26 16 44 12 14 48 22 46 40 24 42 18 20 38 6 8 10 0 null 4 82 66 84 86 78 80 76 62 64 90 92 94 88 96 98 

除了以上在ForEach里面添加集合元素会出现这种问题,以下这种方式也会:

              listOfIntegers
                .parallelStream()
                .map(e -> {
                    parallelStorage.add(e);
                    return e;
                })
                .forEachOrdered(e -> System.out.print(e + " "));

两个问题:
1.为什么parallelStorage的大小不固定?
2.为什么parallelStorage会有null元素?

最初我以为是因为主线程执行完成后并行流中的线程并未结束,sleep了主线程后发现结果并没有发生改变,其实我们可以认为ArrayList内部维护了一个数组Arr其定义一个变量 n用以表式这个数组的大小那么向这个ArrayList中存储数据的过程可以分解为这么几步:

1.读取数组的长度存入n
2.向这个数组中储入元素arr[n]=a
3.将n+1
4.保存n
而对于parrallelStorage元素数量不固定的原因就是多线程有可能同时读取到相同的下标n同时赋值,这样就会出现元素缺失的问题了
如何解决这个问题呢?我们可以将其转化为一个同步集合也就是

Collections.synchronizedList(new ArrayList<>()) 

在使用并行流的时候是无法保证元素的顺序的,也就是即使你用了同步集合也只能保证元素都正确但无法保证其中的顺序。

除了以上这种方式,还有什么方法可以防止并行流出现线程不安全操作?

那就是最后调用collect(Collectors.tolist()),这种收集起来所有元素到新集合是线程安全的。Demo如下:

public static void collectFun() {
        List<Integer> listOfIntegers =
                new ArrayList<>();

        for (int i = 0; i <100; i++) {
            listOfIntegers.add(i);
        }

        List<Integer> parallelStorage = listOfIntegers
                .parallelStream()
                .filter(i -> i % 2 == 0)
                .collect(Collectors.toList());


        System.out.println();

        parallelStorage
                .stream()
                .forEachOrdered(e -> System.out.print(e + " "));

        System.out.println();
        System.out.println("Sleep 5 sec");
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        parallelStorage
                .stream()
                .forEachOrdered(e -> System.out.print(e + " "));
    }

程序运行结果如下:

0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94 96 98 
Sleep 5 sec
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94 96 98 

不光没有出现Null和数量不一致问题,还排序了!所以,在采用并行流收集元素到集合中时,最好调用collect方法,一定不要采用Foreach方法或者map方法。

免责声明:文章转载自《Java8使用并行流(ParallelStream)注意事项》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇软件产品升级流程介绍 --致敬为发布升级工作奋斗到凌晨的程序猿临时表和全局临时表下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Java 之 Scanner 类

一、Scanner 类 Scanner 是一个可以解析基本类型和字符串的简单文本扫描器。 Demo: 1 Scanner sc = newScanner(System.in); 2 int i = sc.nextInt(); System.in 系统输入指的通过键盘录入数据。 二、引用类型使用步骤   1、导包 使用 import 关键字导包,在类的所有代...

C#远程访问linux(ubuntu)或windows的mysql数据库

 1、远程访问数据库大概模型 2、mysql在win7、linux上如何设置:2.1、分配权限(linux和win7) 进行mysql命令行,进行分配权限、执行 GRANTALLPRIVILEGESON*.*TO'Lucy'@'192.168.1.102' IDENTIFIED BY'123'WITHGRANTOPTION; ALL PRIVILEGE...

为DataGrid添加自动编号功能

下面的代码实现在DataGrid中添加自动编号的功能,主要是在数据绑定时利用Item属性。 DataGridWithLine.aspx <%@ Page Language="vb" AutoEventWireup="false" Codebehind="DataGridWithLine.aspx.vb"Inherits="aspxWeb.DataG...

java基础1-变量+包机制+javadoc命令

1.sout+tab:System.out.println的快捷键 2.强制转换:(高转低) 由低到高:byte,short,char→int→long→float→double 运算时,先转为相同类型再进行计算 //强制转换(类型)变量名 int i=128; byte b = (byte)i; System.out.println(i);/...

Val简介(来源维基百科)

1.中文版 网址:http://zh.wikipedia.org/wiki/%E5%8F%AF%E5%8F%98%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80 可变汇编语言(Variable Assembly Language,VAL)是一个设计给Unimation Inc.工业机器人用的电脑控制系统及编程语言。VAL机器人语言是会...

C#面向对象思想计算两点之间距离

题目为计算两点之间距离。 面向过程的思维方式,两点的横坐标之差,纵坐标之差,平方求和,再开跟,得到两点之间距离。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; names...