先看一段代码,分别为串行遍历、并行遍历、并行加锁遍历:
private static List<Integer> list1 = new ArrayList<>(); private static List<Integer> list2 = new ArrayList<>(); private static List<Integer> list3 = new ArrayList<>(); private static Lock lock = new ReentrantLock(); public static void testStrem(){ int max = 10000; long startTime1=System.currentTimeMillis(); //获取开始时间 //串行遍历 IntStream.range(0,max).forEach(i->{ try{ list1.add(i); } finally { } }); long endTime1=System.currentTimeMillis(); long startTime2=System.currentTimeMillis(); //并行执行 IntStream.range(0,max).parallel().forEach(i->{ try{ list2.add(i); } finally { } }); long endTime2=System.currentTimeMillis(); long startTime3=System.currentTimeMillis(); //加锁并行执行 IntStream.range(0,max).parallel().forEach(i->{ lock.lock(); try{ list3.add(i); } finally { lock.unlock(); } }); long endTime3=System.currentTimeMillis(); System.out.println("串行执行的大小:" + list1.size()+" 程序运行时间: "+(endTime1-startTime1)+"ms"); System.out.println("并行执行的大小:" + list2.size()+" 程序运行时间: "+(endTime2-startTime2)+"ms"); System.out.println("加锁并行执行的大小:" + list3.size()+" 程序运行时间: "+(endTime3-startTime3)+"ms"); }
执行结果:
串行执行的大小:10000 程序运行时间: 71ms 并行执行的大小:9567 程序运行时间: 10ms 加锁并行执行的大小:10000 程序运行时间: 5ms ---------------------------------------------------
串行执行的大小:100000 程序运行时间: 75ms 并行执行的大小:74661 程序运行时间: 13ms 加锁并行执行的大小:100000 程序运行时间: 18ms
从执行结果来看,parallelStream并非线程安全,所以配合锁既可以保证线程安全,还可以提高效率。
parallelStream就是其中一个非常使用的特性。我总结了几个好处:
- 代码优雅,可以使用lambda表达式,原本几句代码现在一句可以搞定
- 运用多核特性(forkAndJoin)并行处理,大幅提高效率。
parallelStream使用时需要注意的点:
- parallelStream 只是做到别浪费cpu,假如本身电脑cpu的负载很大,那还到处用parallel,那并不能起到作用
- 不要在多线程中使用parallelStream,原因同上类似,大家都抢着cpu是没有提升效果,反而还会加大线程切换开销
- 会带来不确定性,请确保每条处理无状态且没有关联
- 考虑nq模型:n可用的数据量,q针对每个数据元素执行的计算量乘积 N*Q 越大,就越有可能获得并行提速。N x Q > 10000 就会获得有效提升