Java8 parallelStream——共享线程池对性能解析

最近做压测中发现一个应用中cpu过高,导致接口超时rt情况有些不大稳定,jstack打印线程一直在parallelStream相关的代码出进行计算。故对parallelStream相关做一下研究,找一下优化方法。

 

java8并行流parallelStream,相信很多人都喜欢用,特别方便简单。但是有多少人真正知道里面采用的共享线程池对密集型任务,高并发下的性能影响呢

可能你的一个应用里面,有几个密集型的计算任务,但是相信你的应用里面还有更多的非密集型的任务吧。如果密集型的任务一直占用着线程资源,那么对你系统造成的压力会有多少?

下面直接贴代码


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;

/**
 * created by devin on 2019/9/30
 */
public class ForkJoinPoolTest {
    //最大可用的CPU核数
    public static final int PROCESSORS = Runtime.getRuntime().availableProcessors();

    public static void main(String[] args) throws InterruptedException {
        //定义初始的2个数组
        List firstRange = new ArrayList<>();
        List secondRange = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            firstRange.add(i);
            secondRange.add(i);
        }
        System.out.println(ForkJoinPool.getCommonPoolParallelism());

        //注意查看线程sleep时候,有几个线程在打印数据。特意sleep时间间隔较长
        test1(firstRange, secondRange);
//        test2(firstRange, secondRange);
        //保证程序一直存活的,打印数据
        while (true) {
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(500000);
            System.out.println();
        }
    }

    //使用java8 parallelStream共享缓存
    public static void test1(List firstRange, List secondRange) {
        Runnable firstTask = () -> {
            firstRange.parallelStream().forEach((number) -> {
                try {
                    // do something slow
                    System.out.println("test1.1  --  >" + Thread.currentThread().getName() + "    num=" + number);
                    Thread.sleep(50000);//间歇50s是为了方便看一次执行的线程数量
                } catch (InterruptedException e) {
                }
            });
        };

        Runnable secondTask = () -> {
            secondRange.parallelStream().forEach((number) -> {
                try {
                    // do something slow
                    System.out.println("test1.2  --  >" + Thread.currentThread().getName() + "    num=" + number);
                    Thread.sleep(50000);
                } catch (InterruptedException e) {
                }
            });
        };
        //new一个线程,模拟不同的请求,以免认为是顺序调用
        new Thread(new Runnable() {
            @Override
            public void run() {
                firstTask.run();
            }
        },"thread-111111").start();
        //new一个线程,模拟不同的请求,以免认为是顺序调用
        new Thread(new Runnable() {
            @Override
            public void run() {
                secondTask.run();
            }
        }, "thread-222222").start();

    }

    //包装线程池,不使用共享线程池,因为不是每个线程都必须是计算密集型的
    public static void test2(List firstRange, List secondRange) {
        //线程池大小可以根据cpu个数来定义
        ForkJoinPool forkJoinPool = new ForkJoinPool(PROCESSORS);
        forkJoinPool.submit(() -> {
            firstRange.parallelStream().forEach((number) -> {
                try {
                    System.out.println("test2.1  --  >" + Thread.currentThread().getName() + "    num=" + number);
                    Thread.sleep(50000);
                } catch (InterruptedException e) {
                }
            });
        });
        ForkJoinPool forkJoinPool2 = new ForkJoinPool(PROCESSORS);
        forkJoinPool2.submit(() -> {
            secondRange.parallelStream().forEach((number) -> {
                try {
                    System.out.println("test2.2  --  >" + Thread.currentThread().getName() + "    num=" + number);
                    Thread.sleep(50000);
                } catch (InterruptedException e) {
                }
            });
        });
    }
}

我们直接来看看test1的执行结果。sleep时间较长,是为了更好地结果输出

Java8 parallelStream——共享线程池对性能解析_第1张图片

我们会发现,共享线程池,每次只有三个共享线程执行了任务,如果你一个应用都使用到的是共享线程池,而其中有的是rpc调用呢,线程是会被一直占用资源的,会一定程度的降低应用的整体性能。

而test2是我们对该种情况的一个优化,我们如果对其进行包装,用用线程池包装,就会解决这个问题。

Java8 parallelStream——共享线程池对性能解析_第2张图片

那么你的一个应用中就不会因为共享线程的阻塞,或者等待rpc请求调用而导致共享的线程无法释放,而导致应用整体性能降低的问题。

================================

另外,细心的同学会发现,为什么共享的线程是3个?这个是系统默认的个数:Runtime.getRuntime().availableProcessors()-1个(ForkJoinPool.getCommonPoolParallelism())。测试电脑是4core的,所以是3个线程。

以下是源码:

Java8 parallelStream——共享线程池对性能解析_第3张图片

 

另外对于密集型应用该设置多大线程池呢?有的文章介绍是(cpu核数 +1)。非计算密集型的 2 * cpu核数。供参考

你可能感兴趣的:(java,多线程)