Java VisualVM 观察自定义线程池

背景

在一次使用自定义线程池中,我发现了部分代码是用完没有进行shutdown的,这样是否会造成资源浪费?

 

 

一、单体架构下:测试自定义线程池:

 

1.1、首先我们把运行时时的线程数量打印出来,放上代码: 然后打开Java VisualVM进行观测:


 

public class ThreadTest {

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

        AtomicInteger atomicInteger = new AtomicInteger();
        for (int i = 0; i < 10; i++) {
            test(atomicInteger);
        }
    }

    public static void  test(AtomicInteger atomicInteger) throws InterruptedException {
        //用于获取到本java进程,进而获取总线程数
        RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
        String jvmName = runtimeBean.getName();
        System.out.println("JVM Name = " + jvmName);
        long pid = Long.valueOf(jvmName.split("@")[0]);
        System.out.println("JVM PID  = " + pid);
        ThreadMXBean bean = ManagementFactory.getThreadMXBean();
        int n = 3000000;
        for (int i = 0; i < n; i++) {
ThreadPoolExecutor executor
= new ThreadPoolExecutor(10, 20, 1000, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); for (int j = 0; j < 10; j++) { executor.execute(() -> { System.out.println(atomicInteger.getAndIncrement()+"==="+ Thread.currentThread().getName()+"==当前线程总数为:" + bean.getThreadCount()); }); }
        // 此处控制是否释放运行完线程
executor.shutdown(); } Thread.sleep(10000); System.out.println("线程总数为 = " + bean.getThreadCount()); } }

 

1.2 我们测试看看实际情况:

 可以看到线程池有时候还没来得及释放的时候, 峰值是400多

 

 

 

1.3非峰值很稳定在20多附近。

 

 

 

 

1.4附上内存图等情况:

 

 

 

2.1

 第二次,因为如果不做线程睡眠,直接把服务干挂了,连着visualVm都启动不了,   我们代码需要调整下:

 

 

public class ThreadTest {

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

        AtomicInteger atomicInteger = new AtomicInteger();
        for (int i = 0; i < 10; i++) {
            test(atomicInteger);
        }
    }

    public static void  test(AtomicInteger atomicInteger) throws InterruptedException {
        //用于获取到本java进程,进而获取总线程数
        RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
        String jvmName = runtimeBean.getName();
        System.out.println("JVM Name = " + jvmName);
        long pid = Long.valueOf(jvmName.split("@")[0]);
        System.out.println("JVM PID  = " + pid);
        ThreadMXBean bean = ManagementFactory.getThreadMXBean();
        int n = 3000000;
        for (int i = 0; i < n; i++) {
      // 千万不要再这里new了线程,然后不shutdown,数量大的话你会直接挂的

ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 20, 1000, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); for (int j = 0; j < 10; j++) {
          // 此处线程睡眠
                Thread.sleep(500);
                executor.execute(() -> {
                    System.out.println(atomicInteger.getAndIncrement()+"==="+ Thread.currentThread().getName()+"==当前线程总数为:" + bean.getThreadCount());
                });
            }
            // 此处控制线程运行完是否释放
          //  executor.shutdown();
        }
        Thread.sleep(10000);
        System.out.println("线程总数为 = " + bean.getThreadCount());
    }

}

 

 

 

2.2调整后效果如下:  你发现线程数量永远是网上升的

 

 

 

 

 2.3回收不了,已经越来越多了。

 

 

 

 

 

2.4附上内存情况

 

 

 

3.1我们把new的动作移出来:

 

public class ThreadTest {

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

        AtomicInteger atomicInteger = new AtomicInteger();
//        for (int i = 0; i < 10; i++) {
            test(atomicInteger);
//        }
    }

    public static void  test(AtomicInteger atomicInteger) throws InterruptedException {
        //用于获取到本java进程,进而获取总线程数
        RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
        String jvmName = runtimeBean.getName();
        System.out.println("JVM Name = " + jvmName);
        long pid = Long.valueOf(jvmName.split("@")[0]);
        System.out.println("JVM PID  = " + pid);
        ThreadMXBean bean = ManagementFactory.getThreadMXBean();
        int n = 3000000;
        ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 20, 1000, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < 10; j++) {
                Thread.sleep(100);
                executor.execute(() -> {
                    System.out.println(atomicInteger.getAndIncrement()+"==="+ Thread.currentThread().getName()+"==当前线程总数为:" + bean.getThreadCount());
                });
            }
            // 此处控制线程运行完是否释放
        }
//        executor.shutdown();
        Thread.sleep(10000);
        System.out.println("线程总数为 = " + bean.getThreadCount());
    }

}

 

3.2 线程数量如下 

 

 

 

可以看到,只要不在循环中去new你的线程池就不会爆炸。 所以一定要去手动shutdown,不然很可能就会挂。

 

 

 

 

 三、 我们用自带的线程池子测试:

 

①   第一种线程池: Executors.newSingleThreadExecutor();

②   第二种线程池: Executors.newCachedThreadPool();

代码如下:

public class QfRunner {


    public static void main(String[] args) throws InterruptedException {
        getOne();
    }

    public static void getOne() throws InterruptedException {
        System.out.println(Thread.currentThread().getName()+":main");

        ArrayList<String> strings = new ArrayList<>();

        // 这个池子没问题,稳定在2-4个线程
     //   Executors.newSingleThreadExecutor();
        // 这个池子没问题,稳定在2-4个线程
        ExecutorService pool = Executors.newCachedThreadPool();

        for (int i = 0; i < 100; i++) {
            Thread.sleep(100);
            pool.execute(new QfFlowLauncher(strings));
        }
//        pool.shutdown();
    }


    static class QfFlowLauncher implements Runnable{
        private List<String> leaveFlowNoList;

        public QfFlowLauncher(List<String> leaveFlowNoList){
            this.leaveFlowNoList = leaveFlowNoList;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+":run");
        }
    }
}

 

 

可以看到,线程数量是没变化的,堆的大小也没什么变化

 

 

 

 我们把它

//        pool.shutdown();

放开注释  之后继续看, 效果是一样的:

 

 

 

③  第三种线程池:

ExecutorService pool = Executors.newFixedThreadPool(10);

发现数量也是稳定,就算shutDown没有回收,效果也是一样的

 

 

 

四、测试自定义线程池:

 

4.1 上来就1000个并发量:

public class ThreadTest {

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

        AtomicInteger atomicInteger = new AtomicInteger();
        for (int i = 0; i < 1000; i++) {
       new Thread(()-> {
           try {
               test(atomicInteger);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }).start();
        }
    }

    public static void  test(AtomicInteger atomicInteger) throws InterruptedException {
        //用于获取到本java进程,进而获取总线程数
        RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
        String jvmName = runtimeBean.getName();
        System.out.println("JVM Name = " + jvmName);
        long pid = Long.valueOf(jvmName.split("@")[0]);
        System.out.println("JVM PID  = " + pid);
        ThreadMXBean bean = ManagementFactory.getThreadMXBean();
        int n = 3000000;
        ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 20, 1000, TimeUnit.SECONDS, new LinkedBlockingDeque<>());

            for (int j = 0; j < 10; j++) {
                Thread.sleep(100);
                executor.execute(() -> {
                    System.out.println(atomicInteger.getAndIncrement()+"==="+ Thread.currentThread().getName()+"==当前线程总数为:" + bean.getThreadCount());
                });
            }
            // 此处控制线程运行完是否释放

        executor.shutdown();
        Thread.sleep(10000);
        System.out.println("线程总数为 = " + bean.getThreadCount());
    }

}

 

4.2我们看我们的池子,线程数都1万个了, :

 

 

 

5.1  再利用jmeter测试之后, 发现的确和上述一样,并发高的情况下,发现运行正常,

1秒1000的并发量下, 还是没问题的代码如下:

 

    public  void  testadd(AtomicInteger atomicInteger) throws InterruptedException {
        //用于获取到本java进程,进而获取总线程数
        RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
        String jvmName = runtimeBean.getName();
        System.out.println("JVM Name = " + jvmName);
        long pid = Long.valueOf(jvmName.split("@")[0]);
        System.out.println("JVM PID  = " + pid);
        ThreadMXBean bean = ManagementFactory.getThreadMXBean();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200000, 10000, TimeUnit.SECONDS, new LinkedBlockingDeque<>());

        executor.execute(() -> {
                System.out.println(atomicInteger.getAndIncrement()+"==="+ Thread.currentThread().getName()+"==当前线程总数为:" + bean.getThreadCount());
            });
        // 此处控制线程运行完是否释放
        executor.shutdown();
        Thread.sleep(10000);
        System.out.println("线程总数为 = " + bean.getThreadCount());
    }

 

 5.2 测试情况如图:

 

 

 

6. 如果我们把他弄成static情况下, 会直接爆了,所以千万不要写成static的:

private  static   ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 20, 1000, TimeUnit.SECONDS, new LinkedBlockingDeque<>());

 

posted @ 2021-07-01 14:42  Rick_Leee  阅读(367)  评论(0)    收藏  举报