CompletableFuture在性能优化中的使用

CompletableFuture在性能优化中的使用

    • 实践:打印一个集合内的所有内容
    • ParallelStream
    • CompletableFutrue
    • 对比
    • 注意事项
    • ParallelStream自定义线程池

实践:打印一个集合内的所有内容

数据准备:

    private static final int COUNT = 100000;
    private Vector<String> datas;
    @Before
    public void init() {
        datas = new Vector<>();
        for (int i = 0; i < COUNT; i++) {
            datas.add("data-" + i);
        }
    }

ParallelStream

使用ParallelStream并行打印

    @Test
    public void testParallelStream(){
        datas.parallelStream().forEach(p->{
            System.out.println("p=" + p + "," + Thread.currentThread().getName());
        });
    }

打印结果

p=data-39215,ForkJoinPool.commonPool-worker-3
p=data-70897,main
p=data-70898,main
p=data-70899,main
p=data-70900,main
p=data-70901,main
p=data-70902,main
p=data-70903,main
p=data-70904,main
p=data-4288,ForkJoinPool.commonPool-worker-1
p=data-4289,ForkJoinPool.commonPool-worker-1

通过打印结果可以其使用了ForJoin的线程进行多线程打印逻辑,并且线程号递增至p=data-688,ForkJoinPool.commonPool-worker-7是由于测试机的CPU核心数就是8

Runtime.getRuntime().availableProcessors()

所以main+上7个ForkJoinPool.commonPool-worker等于CPU的核心数,验证了ParalleStream默认使用的是CPU核心数的线程池大小。

CompletableFutrue

使用CompletableFutrue则需要将数据集自行拆分,进行多线程的处理

    @Test
    public void testCompletableFuture() {
        ExecutorService nodeExecutor = new ThreadPoolExecutor(10, 100, 100L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), UserThreadFactory.build("discovery-l3-node"));
        List<List<String>> mePartition = Lists.partition(datas, 20);
        List<CompletableFuture<Void>> printFuture = mePartition.stream().map(ids -> CompletableFuture.runAsync(() -> {
            ids.forEach(p -> {
                System.out.println("p=" + p + "," + Thread.currentThread().getName());
            });
        }, nodeExecutor)).collect(Collectors.toList());
        CompletableFuture.allOf(printFuture.toArray(new CompletableFuture[]{})).join();
    }

代码中通过Lists.partition对集合进行了分组,然后利用核心线程数为10的线程进行数据的打印,结果如下:

p=data-196,discovery-l3-node-1-thread-10
p=data-197,discovery-l3-node-1-thread-10
p=data-198,discovery-l3-node-1-thread-10
p=data-199,discovery-l3-node-1-thread-10
p=data-200,discovery-l3-node-1-thread-1
p=data-201,discovery-l3-node-1-thread-1
p=data-202,discovery-l3-node-1-thread-1

对比

上述两种方式的特点很明显

  • ParallelStream编码方便
  • CompletableFuture灵活自定义线程,当然ParallelStream也可以使用自定义线程,此外CompletableFuture提供各类有时序的并发控制方法

注意事项

在多个CompletableFuture里如果一个线程安全的集合如vector操作,会导致多个CompletableFuture针对vector实际上是串行

ParallelStream自定义线程池

    @Test
    public void testParallelStream2() throws ExecutionException, InterruptedException {
        ForkJoinPool forkJoinPool = new ForkJoinPool(10);
        ForkJoinTask<?> submit = forkJoinPool.submit(() -> {
            datas.parallelStream().forEach(p -> {
                System.out.println("p=" + p + "," + Thread.currentThread().getName());
            });
        });
        submit.get();
    }

你可能感兴趣的:(Java基础)