隐藏并行流ParallelStream中隐藏的陷阱( 二 )


Lambda/Stream其实并不是天然线程安全的 , 线程安全的前提是它们本身被线程封闭调用 , 并且不引入多线程环境 , 像使用了并行流 , 本质就是引入了多线程环境 。 所以 , 在开发功能的时候 , 需要仔细思考一下:

  1. 是否真的有必要使用Lambda和流式编程?
  2. 是否真的有必要用到并行流?如果使用了并行流 , 是否需要考虑引入额外的同步机制 , 例如锁?
  3. 如果引入了额外的同步机制 , 是否考虑是强行使用并行流 , 违反了并行流设计的初衷?
  4. 其实并发并不能提高性能 , 只能提高吞吐量 , 应该着重去发现和优化性能瓶颈 , 而不是拼命地把上游改造成并发调用 。
?
笔者有代码洁癖 , 当时还发现了上面的代码存在映射操作 , 正确来说应该使用map()函数 , 而不是forEach()去遍历元素重新装进去另一个列表 , 方法中的逻辑体现了原开发者其实对Lambda一知半解 。
?
小结 回到最初那个问题 , 其实使用并行流也可以保证执行结果和预期一致 , 不过一定需要引入额外的同步机制 , 例如这里使用「监视器」进行同步:
public class ParallelStreamMain { public static void main(String[] args) throws Exception { List&ltList&gt array = new ArrayList&lt&gt() List item1 = new ArrayList&lt&gt() List item2 = new ArrayList&lt&gt() List target = new ArrayList&lt&gt(100) array.add(item1) array.add(item2) final Object monitor = new Object() array.parallelStream().forEach(x -&gt { synchronized (monitor) { for (int i = 0 i &lt 100000 i++) { target.add(i) } } }) System.out.println(target.size()) }}
上面的方法无论执行多少次 , 最终都只会输出:200000 。 这里在并行流中添加同步代码块的逻辑看起来确实比较滑稽 , 仅仅是为了说明如果在多线程环境下 , 对一个容器进行元素增加或者修改 , 只有添加额外的同步机制 , 才能保证最终的结果是符合预期的 。 ParallelStream是一个十分优秀的设计 , 但是需要考量其适用的场景 , 避免踏进自己为自己埋下的并发陷阱 。
【隐藏并行流ParallelStream中隐藏的陷阱】


推荐阅读