线程(十五)---CompletableFuture、并行流(parallelStream)

写在前面:各位看到此博客的小伙伴,如有不对的地方请及时通过私信我或者评论此博客的方式指出,以免误人子弟。多谢!   

   记录一下使用并行流、CompletableFuture、及普通处理方式的效率问题,创建一个taskList表示要处理的任务列表,新建task方法,sleep两秒,模拟下处理每个任务所需时间,先测试一下使用并行流,直接上代码:

public class ParallelStreamAndCompletableFuture {

    static final List<Object> taskList = Arrays.asList(1,2,3,4,5,6,7,8,9,10);

    public static void main(String[] args) throws Exception{
        

        long startTime = System.currentTimeMillis();
        List<Object> result =
                taskList.parallelStream().map(s -> task(s)).collect(Collectors.toList());
        System.out.println(result);
        long endTime = System.currentTimeMillis();
        System.out.println("使用 parallelStream 耗时:" + (endTime - startTime));

    }

    static public Object task(Object i) {
        System.out.println(Thread.currentThread().getName());
        try {
            Thread.sleep(2000);
        } catch (final InterruptedException e) {
            throw new RuntimeException(e);
        }
        return i;
    }

}

执行结果如下:

可以看到使用并行流处理用时6秒多点,比起挨个处理已经块很多了,并且并行流使用简单,不过从打印上看,并行流内部也是使用forkJojin线程池实现的,前面说到,ForkJoinPool 的线程数默认是 CPU 的核心数。但是,最好不要所有业务共用一个线程池,因为,一旦有任务执行一些很慢的 I/O 操作,就会导致线程池中所有线程都阻塞在 I/O 操作上,从而造成线程饥饿,进而影响整个系统的性能。所以使用并行流的时候还是需要注意一下的。

  使用CompletableFuture测试下,代码如下:

public class ParallelStreamAndCompletableFuture {

    static final List<Object> taskList = Arrays.asList(1,2,3,4,5,6,7,8,9,10);

    public static void main(String[] args) throws Exception{

        long startTime = System.currentTimeMillis();
        List<CompletableFuture<Object>> futures =
                taskList.stream().map(s -> CompletableFuture.supplyAsync(() -> task(s))).collect(Collectors.toList());
        List<Object> result = futures.stream().map(s -> s.join()).collect(Collectors.toList());
        System.out.println(result);
        long endTime = System.currentTimeMillis();
        System.out.println("使用 CompletableFuture 耗时:" + (endTime - startTime));

       
    }

    static public Object task(Object i) {
        System.out.println(Thread.currentThread().getName());
        try {
            Thread.sleep(2000);
        } catch (final InterruptedException e) {
            throw new RuntimeException(e);
        }
        return i;
    }

}

执行结果如下:

话费的时间比并行流多2秒左右,说明fork join的过程确实也是很消耗时间的,在介绍fork/join的时候也说过,使用ForkJoin时,任务的量一定要大,否则太小,forkjoin拆分合并任务也是需要时间的,对于计算量比较小的任务,拆分合并所花费的时间可能会大于计算时间,效率不一定会高。

CompletableFuture可以指定线程池,指定线程池后代码如下:

public class ParallelStreamAndCompletableFuture {

    static final List<Object> taskList = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws Exception{
        
        long startTime = System.currentTimeMillis();
        List<CompletableFuture<Object>> futures =
                taskList.stream().map(s -> CompletableFuture.supplyAsync(() -> task(s),executor)).collect(Collectors.toList());
        List<Object> result = futures.stream().map(s -> s.join()).collect(Collectors.toList());
        System.out.println(result);
        long endTime = System.currentTimeMillis();
        System.out.println("使用 CompletableFuture+线程池 耗时:" + (endTime - startTime));
    }

    static public Object task(Object i) {
        System.out.println(Thread.currentThread().getName());
        try {
            Thread.sleep(2000);
        } catch (final InterruptedException e) {
            throw new RuntimeException(e);
        }
        return i;
    }

}

执行结果如下:

只用了两秒,当然也是因为我开了10个线程,不过这也是使用CompletableFuture的一个好处吧,可以自定义线程池的大小,如果设置合理,对性能提示还是很大的。

PS:注意真正用的时候不要使用Executors创建线程池。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值