线程池监控2-监控线程池状态、线程数量和队列任务数量等
1.实现原理
这篇博文是基于线程池监控1-监控任务执行时间,原理是:创建一个固定时间间隔执行的线程,来记录线程池的池状态、线程数量和队列任务数量等,具体方案:使用单例类缓存所有创建的线程池对象,类创建时启动定时任务线程,定期遍历缓存中线程池,记录线程池信息。
使用单例类缓存所有创建的线程池对象,具体操作:在线程池创建的时候添加到缓存中,shutdown的时候从缓存中删除,只用1个定时线程就可以监控多个线程池,非常方便。
2.实现代码
package com.xkzhangsan.thread.pool.monitor; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * 全局监控 <br> * 1.定期记录线程池基本信息 <br> * * @author xkzhangsan */ public class GlobalMonitor { private static volatile GlobalMonitor instance; private static final ConcurrentHashMap<String, ThreadPoolMonitor> threadPoolMonitorMap = new ConcurrentHashMap<>(); private static final ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1); private GlobalMonitor() { scheduledThreadPoolExecutor.scheduleAtFixedRate(new PoolInfoRunnable(), 10, 10, TimeUnit.SECONDS); } public static GlobalMonitor getInstance() { if (instance == null) { synchronized (GlobalMonitor.class) { if (instance == null) { instance = new GlobalMonitor(); } } } return instance; } public void put(String poolName, ThreadPoolMonitor threadPoolMonitor) { threadPoolMonitorMap.put(poolName, threadPoolMonitor); } public void remove(String poolName) { threadPoolMonitorMap.remove(poolName); } static class PoolInfoRunnable implements Runnable { @Override public void run() { threadPoolMonitorMap.forEach((poolName, threadPoolMonitor) -> { int currentPoolSize = threadPoolMonitor.getPoolSize(); int queueSize = threadPoolMonitor.getQueue().size(); System.out.println("poolName:" + poolName + " status:" + threadPoolMonitor.getStatus() + " corePoolSize:" + threadPoolMonitor.getCorePoolSize() + " maximumPoolSize:" + threadPoolMonitor.getMaximumPoolSize() + " currentPoolSize:" + currentPoolSize + " queueCapacity:" + threadPoolMonitor.getQueueCapacity() + " queueSize:" + queueSize); }); } } }
获取线程池状态
这里参考了ThreadPoolExecutor的toString(),返回Running、Shutting down、Terminated 三种状态。
见:ThreadPoolMonitor类
public String getStatus() { if (super.isTerminated()) { return "Terminated"; } else if (super.isShutdown()) { return "Shutting down"; } else { return "Running"; } }
获取队列总容量
创建ThreadPoolExecutor时,传入的BlockingQueue<Runnable> workQueue,无法直接获取总容量,ThreadPoolExecutor又没有直接获取总容量的方法,
这里想到另一个方法,Queue的remainingCapacity()返回当前队列剩余容量,原理:总容量-队列size,所以,在刚创建时size为0,返回的就时总容量。
见:ThreadPoolMonitor类
private void init(String poolName, MonitorLevelEnum monitorLevel) { this.poolName = poolName; this.monitorLevel = monitorLevel; this.taskStartTimeMap = new ConcurrentHashMap<>(); if (isPoolMonitor()) { GlobalMonitor.getInstance().put(poolName, this); } this.queueCapacity = super.getQueue().remainingCapacity(); } public int getQueueCapacity() { return this.queueCapacity; }
3.测试运行
3.1 测试代码
package com.xkzhangsan.thread.pool.monitor; import com.xkzhangsan.thread.pool.monitor.constant.MonitorLevelEnum; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.TimeUnit; public class ThreadPoolMonitorTest { public static void main(String[] args) throws InterruptedException { poolMonitor(); } public static void poolMonitor() throws InterruptedException { ThreadPoolMonitor threadPoolMonitor = new ThreadPoolMonitor(1, 3, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1000), "test", MonitorLevelEnum.POOL); for (int i = 0; i < 100; i++) { int finalI = i; threadPoolMonitor.execute(() -> { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(finalI); }); } //因为调用shutdown方法会将threadPoolMonitor从监控缓存中删除,这里sleep 100s TimeUnit.SECONDS.sleep(100); //线程池必须手动关闭,否则一直运行 threadPoolMonitor.shutdown(); } }
3.2 测试结果
0 1 2 poolName:test status:Shutting down corePoolSize:1 maximumPoolSize:3 currentPoolSize:1 queueCapacity:1000 queueSize:96 3 4 5 poolName:test status:Shutting down corePoolSize:1 maximumPoolSize:3 currentPoolSize:1 queueCapacity:1000 queueSize:93 6 7 8 poolName:test status:Shutting down corePoolSize:1 maximumPoolSize:3 currentPoolSize:1 queueCapacity:1000 queueSize:90 9
线程sleep 3s,监控日志10s打印一次,队列中的任务在不断被消费减少。
源代码地址:https://github.com/xkzhangsan/thread-pool-monitor
https://gitee.com/xkzhangsan/thread-pool-monitor
寻找撬动地球的支点(解决问题的方案),杠杆(Java等编程语言)已经有了。xkzhangsan