JDK8新特性之Stream流

简介: JDK8新特性之Stream流-并行的Stream流以及案例实操

一.JDK8新特性之Stream流-并行的Stream流以及案例实操

image.png

二. 并行的Stream流

2.1 串行的Stream流

我们前面使用的Stream流都是串行,也就是在一个线程上面执行。

/**
     * 串行流
     */
    @Test
    public void test01(){
   
   
        long count = Stream.of(1, 2, 3, 4, 5, 6)
                .filter(s -> {
   
   
                    System.out.println(Thread.currentThread() + "" + s);
                    return s % 2 == 0;
                }).count();
        System.out.println(count);
    }

2.2 并行流

parallelStream其实就是一个并行执行的流,它通过默认的ForkJoinPool,可以提高多线程任务的速度。

2.2.1 获取并行流俩种方式

我们可以通过两种方式来获取并行流。

  1. 通过List接口中的parallelStream方法来获取
  2. 通过已有的串行流转换为并行流(parallel)
    /**
     * 获取并行流的两种方式
     */
    @Test
    public void test02(){
   
   
        List<Integer> list = new ArrayList<>();
        // 方式1:通过List接口中的parallelStream方法来获取
        Stream<Integer> integerStream = list.parallelStream();
        // 方式2:通过已有的串行流转换为并行流(parallel)
        Stream<Integer> parallel = Stream.of(1, 3).parallel();
    }

2.2.2 并行流操作

/**
     * 并行流操作
     */
    @Test
    public void test03(){
   
   

        long count = Stream.of(1, 2, 3, 4, 5, 6)
                .parallel() // 将流转换为并发流,Stream处理的时候就会通过多线程处理
                .filter(s -> {
   
   
                    System.out.println(Thread.currentThread() + "" + s);
                    return s % 2 == 0;
                }).count();
        System.out.println(count);
    }

2.3 并行流和串行流对比

我们通过for循环,串行Stream流,并行Stream流来对10000000亿个数字求和。来看消耗时间

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.stream.LongStream;

public class Test03 {
   
   

    private static long times = 300000000;

    private long start;

    /**
     * 程序开始的时候获取当前系统的时间
     */
    @Before
    public void before(){
   
   
        start = System.currentTimeMillis();
    }

    /**
     * 程序结束的时候获取此时系统的时间
     * 计算消耗的时间
     */
    @After
    public void end(){
   
   
        long end = System.currentTimeMillis();
        System.out.println("消耗时间:" + (end - start));
    }


    /**
     * 串行流处理:消耗时间:271
     */
    @Test
    public void test02(){
   
   
        System.out.println("串行流处理:");
        LongStream.rangeClosed(0,times)
                .reduce(0,Long::sum);
    }

    /**
     * 并行流处理 消耗时间:158
     */
    @Test
    public void test03(){
   
   
        System.out.println("并行流处理:");
        LongStream.rangeClosed(0,times)
                .parallel()
                .reduce(0,Long::sum);
    }
}

通过案例我们可以看到parallelStream的效率是最高的。
Stream并行处理的过程会分而治之,也就是将一个大的任务切分成了多个小任务,这表示每个任务都是一个线程操作。
为什么呢?
因为它使用的线程池就是通过分而治之的思想实现的。

2.4 线程安全问题

在多线程的处理下,肯定会出现数据安全问题。如下:

    @Test
    public void test01(){
   
   
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
   
   
            list.add(i);
        }
        List<Integer> listNew = new ArrayList<>();
        // 使用并行流来向集合中添加数据
        list.parallelStream()
                .forEach(listNew::add);
        System.out.println(listNew.size());
    }

运行效果:

image.png

2.5 解决线程安全问题的三种方案

2.5.1 加同步锁

    /**
     * 加同步锁
     */
    @Test
    public void test01(){
   
   
        List<Integer> listNew = new ArrayList<>();
        Object obj = new Object();
        IntStream.rangeClosed(1,1000)
                .parallel()
                .forEach(i->{
   
   
                    synchronized (obj){
   
   
                        listNew.add(i);
                    }

                });
        System.out.println(listNew.size());
    }

2.5.2 使用线程安全的容器

  1. 使用线程安全的容器+synchronized
  2. 将线程不安全的容器转换为线程安全的容器

    
     /**
      * 使用线程安全的容器
      */
     @Test
     public void test02(){
         
         
         Vector v = new Vector();
         Object obj = new Object();
         IntStream.rangeClosed(1,1000)
                 .parallel()
                 .forEach(i->{
         
         
                     synchronized (obj){
         
         
                         v.add(i);
                     }
    
                 });
         System.out.println(v.size());
     }
    
     /**
      * 将线程不安全的容器转换为线程安全的容器
      */
     @Test
     public void test03(){
         
         
         List<Integer> listNew = new ArrayList<>();
         // 将线程不安全的容器包装为线程安全的容器
         List<Integer> synchronizedList = Collections.synchronizedList(listNew);
         Object obj = new Object();
         IntStream.rangeClosed(1,1000)
                 .parallel()
                 .forEach(i->{
         
         
                         synchronizedList.add(i);
                 });
         System.out.println(synchronizedList.size());
     }
    

2.5.3 通过Stream中的toArray/collect操作

通过Stream中的 toArray方法或者 collect方法来操作满足线程安全的要求

    @Test
    public void test05(){
   
   
        List<Integer> listNew = new ArrayList<>();
        Object obj = new Object();
        List<Integer> list = IntStream.rangeClosed(1, 1000)
                .parallel()
                .boxed()
                .collect(Collectors.toList());
        System.out.println(list.size());
    }
相关文章
|
27天前
|
容器
jdk8新特性-详情查看文档
jdk8新特性-详情查看文档
35 3
|
13天前
|
存储 安全 Java
JDK1.8 新的特性
JDK1.8 新的特性
15 0
|
1月前
|
编解码 安全 Java
jdk8新特性-接口和日期处理
jdk8新特性-接口和日期处理
|
2月前
|
API
JDK8的stream有求和方法吗?
【8月更文挑战第20天】JDK8的stream有求和方法吗?
73 3
|
2月前
|
Java API
JDK8到JDK25版本升级的新特性问题之使用Collectors.teeing()来计算一个列表中学生的平均分和总分如何操作
JDK8到JDK25版本升级的新特性问题之使用Collectors.teeing()来计算一个列表中学生的平均分和总分如何操作
|
2月前
|
Java API Apache
JDK8到JDK24版本升级的新特性问题之在Java中,HttpURLConnection有什么局限性,如何解决
JDK8到JDK24版本升级的新特性问题之在Java中,HttpURLConnection有什么局限性,如何解决
|
2月前
|
Oracle Java 关系型数据库
JDK8到JDK29版本升级的新特性问题之未来JDK的升级是否会成为必然趋势,如何理解
JDK8到JDK29版本升级的新特性问题之未来JDK的升级是否会成为必然趋势,如何理解
|
2月前
|
Oracle 安全 Java
JDK8到JDK28版本升级的新特性问题之在Java 15及以后的版本中,密封类和密封接口是怎么工作的
JDK8到JDK28版本升级的新特性问题之在Java 15及以后的版本中,密封类和密封接口是怎么工作的
|
2月前
|
Java API 开发者
JDK8到JDK17版本升级的新特性问题之SpringBoot选择JDK17作为最小支持的Java lts版本意味着什么
JDK8到JDK17版本升级的新特性问题之SpringBoot选择JDK17作为最小支持的Java lts版本意味着什么
JDK8到JDK17版本升级的新特性问题之SpringBoot选择JDK17作为最小支持的Java lts版本意味着什么
|
1月前
|
Java 编译器 API
JDK8新特性--lambda表达式
JDK8的Lambda表达式是Java语言的一大进步。它为Java程序提供了更多的编程方式,让代码更加简洁,也让函数式编程的概念在Java中得到了体现。Lambda表达式与Java 8的其他新特性,如Stream API、新的日期时间API一起,极大地提高了Java编程的效率和乐趣。随着时间的流逝,Java开发者对这些特性的理解和应用将会越来越深入,进一步推动Java语言和应用程序的发展。
11 0