我们知道parallelStream并行流很容易产生线程不安全的问题,基于这个问题,java官方给出两个解决办法:使用收集器collect或者reduce可以保证线程安全。
那么这里我们需要注意的是他保证的只是收集起来的结果的线程安全,如果你像下面的这种:

List<Integer> list = Stream.iterate(1, n -> n + 2)
                .limit(1000000)
                .collect(Collectors.toList());

        List<Integer> midList = Lists.newArrayList();
        list.parallelStream()
                .map(num -> {
                    midList.add(num);
                    return num;
                })
                .collect(Collectors.toList());

他其实是不能保证midList 这个集合add的线程安全的,即使你使用了collect,我们知道ArrayList其实也是一种线程不安全的集合,add也是线程不安全的操作,所以既然并行流只能保证return出去的结果收集器收集起来是线程安全的,那么你在一个map中进行一个线程不安全的操作,当基数特别大的时候就很容易产生问题:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:598)
    at java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:677)
    at java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:735)
    at java.util.stream.ReduceOps$ReduceOp.evaluateParallel(ReduceOps.java:714)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    at ParallelStreamMain.main(ParallelStreamMain.java:26)
Caused by: java.lang.ArrayIndexOutOfBoundsException: 1851
    at java.util.ArrayList.add(ArrayList.java:459)
    at ParallelStreamMain.lambda$main$1(ParallelStreamMain.java:23)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:747)
    at java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:721)
    at java.util.stream.AbstractTask.compute(AbstractTask.java:316)

也就是产生这样的数组越界问题。那么为了解决这个问题我们可能就要约定我们的行为,在一个流里只做一件事,特别是在并行流里,一定要小心这样的线程安全问题。
流只能遍历一次,那我们想复用流要怎么办,我们可以采用封装或者Supplier<T>函数式来解决这个问题

  1. 封装
 private Stream<String> getStream() {
            return Stream.of("d2", "a2", "b1", "b3", "c")
                    .filter(s -> s.startsWith("a"));
        }
  1. Supplier
 Supplier<Stream<String>> streamSupplier =
                    () -> Stream.of("d2", "a2", "b1", "b3", "c")
                            .filter(s -> s.startsWith("a"));
    
            streamSupplier.get().anyMatch(s -> true);   
            streamSupplier.get().noneMatch(s -> true); 

但是这仅仅只是代码的复用,没有性能的提升。