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<>());

浙公网安备 33010602011771号