Stream并发流parallel

本文详细介绍了如何获取并行流,以及并行流存在的线程安全问题及其解决方案,包括使用同步锁、线程安全容器和Stream操作。此外,文章还探讨了并行流的底层原理、使用注意事项和适用场景,如避免I/O密集型操作和保证顺序性的重要性。
摘要由CSDN通过智能技术生成


并行的 Stream 流,即多线程执行的流。

1. 并行流的获取

  • .parallelStream()方法,直接获取并行Stream流
  • .parallel()方法,将串行流转成并行流
public class ParallelStream {
    public static void main(String[] args) {
        //方式一:通过.parallelStream()方法,直接获取并行Stream流
        List<String> list = new ArrayList<>();
        Stream<String> stream = list.parallelStream();
        //方式二:通过.parallel()方法,将串行流转成并行流
        Stream<String> parallelStream = list.stream().parallel();
 
        //并行Stream流测试
        Stream<Integer> stream = Stream.of(4, 5, 6, 3, 21, 56).parallel();
        stream.filter(num->{
            System.out.println(Thread.currentThread().getName() + "--" + num);
            return num>3;
        }).count();
    }
}
输出(发现并行 Stream 流是在 多个线程上执行的):
ForkJoinPool.commonPool-worker-5--6
ForkJoinPool.commonPool-worker-3--4
main--3
ForkJoinPool.commonPool-worker-4--21
ForkJoinPool.commonPool-worker-1--5
ForkJoinPool.commonPool-worker-2--56
数组
String[] array = {"a", "b", "c", "d", "e"};
// 并行流
Stream<String> parallelStreamFromArray = Arrays.stream(array).parallel();

集合
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
// 并行流
Stream<Integer> parallelStreamFromSet = set.parallelStream();

2. 并行流存在线程安全问题

public class ParallelStreamTest05 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            list.add(i);
        }
        System.out.println(list.size());
 
        List<Integer> listNew = new ArrayList<>();
        //使用并行流向集合中添加数据
        list.parallelStream()
                .forEach(s -> listNew.add(s));
        System.out.println(listNew.size());
    }
}
存在线程安全问题!!!

解决并行流线程安全问题:

2.1 加同步锁synchronized

public class ParallelStreamTest {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            list.add(i);
        }
        System.out.println(list.size());
 
        List<Integer> listNew = new ArrayList<>();
        Object object = new Object();
        //使用并行流向集合中添加数据.
        //解决数据安全问题:1.加同步锁
        list.parallelStream()
                .forEach(s -> {
                    synchronized (object){
                        listNew.add(s);
                    }
                });
        System.out.println(listNew.size());
 
        //另一种写法
        List<Integer> list2 = new ArrayList<>();
        Object object2 = new Object();
        IntStream.range(0,1000)
                .parallel()
                .forEach(i -> {
                    synchronized (object2){
                        list2.add(i);
                    }
                });
        System.out.println(list2.size());
    }
}

2.2 使用线程安全的容器synchronizedList

public class ParallelStreamTest06 {
    public static void main(String[] args) {
        //使用线程安全的容器
        List<Integer> list3 = new ArrayList<>();
        List<Integer> list3New = Collections.synchronizedList(list3);
        Object obj = new Object();
        IntStream.range(0,1000)
                .parallel().forEach(s -> list3New.add(s));
        System.out.println(list3New.size());
    }
}

2.3 使用Stream流中的toArray或collect操作

public class ParallelStreamTest06 {
    public static void main(String[] args) {
        //通过Stream中的toArray或collect操作
        List<Integer> list4 = new ArrayList<>();
        List<Integer> collect = IntStream.range(0, 1000)
                .parallel()
                .boxed()
                .collect(Collectors.toList());
        System.out.println(collect.size());
 
    }
}

3. 并行流的底层原理

之所以执行效率高,因为它底层使用的是 Fork/Join 框架。Fork/Join 框架 是在 JDK 7 中引入的,Fork/Join 框架可以将一个大任务拆分为很多小任务来异步执行。

4. 并行流使用注意事项

  • 线程安全性:确保操作是线程安全的。在并行流中,多个线程会同时操作数据,因此需要确保操作的数据结构是线程安全的,或者采取适当的同步措施,避免出现并发修改异常或数据不一致的情况。
  • 性能评估:并行流并不一定在所有情况下都能提升性能,有时甚至可能降低性能。在使用并行流之前,应该进行性能评估和测试,确保并行流确实能够带来性能的提升。
  • 任务分解:并行流会将数据分成多个子任务,并行处理这些子任务。因此,在使用并行流时,应该注意任务的分解是否合理,避免出现任务分配不均衡的情况,导致部分线程长时间等待。
  • 副作用:并行流中的操作可能会产生副作用,例如修改共享状态、I/O操作等。在使用并行流时,应该避免或者谨慎处理可能引起副作用的操作,以确保程序的正确性和可靠性。
  • 并行度调整:Java并行流的并行度默认为CPU核心数,但可以通过调整系统属性 java.util.concurrent.ForkJoinPool.common.parallelism 来修改默认的并行度。根据实际情况,可以根据计算密集型任务或I/O密集型任务来调整并行度,以获得更好的性能表现。
  • 数据量大小:对于小规模的数据集,使用并行流可能会带来额外的开销,因为任务分解和线程管理的开销可能会抵消并行化带来的性能提升。因此,在处理小规模数据集时,可以考虑使用串行流。
  • 避免共享可变状态:避免在并行流中共享可变状态,尽量使用不可变对象或线程安全的数据结构来处理数据,以避免线程安全问题。

5. 总结

  • parallel 并行 Stream 流是 线程不安全 的;
  • parallel 并行 Stream 流使用的场景是 CPU 密集型 的,只是做到别浪费 CPU,假如本身电脑的 CPU 的负载很大,那还到处用并行流,那并不能起到作用;
  • I/O 密集型、磁盘I/O、网络I/O 都属于 I/O 操作,这部分操作时较少消耗 CPU 资源,一般并行流中 不适用于 I/O密集型 的操作,就比如使用并行流进行大批量的消息推送,涉及到了大量 I/O,使用并行流反而慢了很多;
  • 在使用并行流的时候,是 无法保证元素的顺序 的,也就是即使你使用了同步集合也只能保证元素都正确,但无法保证其中的顺序。
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
并发流Parallel Stream)是Java 8引入的一种并行执行的流,它可以通过默认的ForkJoinPool来提高多线程任务的执行速度。与串行流相比,并发流可以在多个线程上同时执行操作,从而加快处理速度。 在Java中获取并发流有两种方式。一种方式是通过将一个集合的stream()方法转换为并发流parallelStream(),例如: List<Integer> list = new ArrayList<>(); Stream<Integer> parallelStream = list.parallelStream(); 另一种方式是直接创建一个并发流,例如: Stream<Integer> parallel = Stream.of(5, 8, 4, 6, 9).parallel(); 并发流的使用方式与串行流相似,可以在流上执行各种操作以获取所需的结果。使用并发流时,可以通过多线程同时处理流中的元素,从而提高处理速度。 总之,并发流是一种方便且有效的方式,可以在处理大数据集时提高代码的执行效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [JDK8新特性07-Stream并行流](https://blog.csdn.net/lu_xin5056/article/details/125124747)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *3* [JAVA-Stream流](https://blog.csdn.net/m0_56161893/article/details/123649402)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值