如何监控线程池
文章目录
线程池两个点需要监控
第一点:线程的变化情况
第二点:任务的变化
用来监控线程变化的方法
方法 | 描述 | 监控方面 |
---|---|---|
getActiveCount() | 获取正在工作的线程数 | 监控线程的变化 |
getPoolSize() | 获取当前存在的线程数 | 监控线程的变化 |
getLargestPoolSize() | 获取历史最大的线程数 | 监控线程的变化 |
getTaskCount() | 获取计划执行的任务总数 | 监控任务的变化 |
getCompletedTaskCount() | 获取已完成的任务数 | 监控任务的变化 |
getQueue() | 获取任务队列 | 监控任务的变化 |
自定义一个带监控的线程池,然后继承ThreadPoolExecutor,重载构造方法
package com.kang.mongodb.pool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import java.util.concurrent.*;
/**
* @Author Emperor Kang
* @ClassName MonitorThhreadPool
* @Description TODO
* @Date 2023/8/7 13:44
* @Version 1.0
* @Motto 让营地比你来时更干净
*/
@Slf4j
public class MonitorThreadPool extends ThreadPoolExecutor {
/**
* 自定义线程池
* @param corePoolSize
* @param maximumPoolSize
* @param keepAliveTime
* @param unit
* @param workQueue
*/
public MonitorThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
public MonitorThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
public MonitorThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
}
public MonitorThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
/**
* 重写before executor方法,该方法每次任务执行前调用,在他内部调用一遍monitor方法,每当有任务执行的时候,输出一次线程池的情况,
* @param t
* @param r
*/
@Override
protected void beforeExecute(Thread t, Runnable r) {
log.info("beforeExecute");
monitor();
}
/**
* 接着重写afterexecutor方法,该方法每次任务完成后调用,在它内部也调用一遍monitor方法,每当有任务完成的时候,输出一次线程池的情况,
* @param r
* @param t
*/
@Override
protected void afterExecute(Runnable r, Throwable t) {
log.info("afterExecute");
monitor();
}
/**
* 最后重写terminated的方法。该方法在线程池关闭前调用,同样的,在它的内部也调用一遍monitor方法,
*/
@Override
protected void terminated() {
log.info("terminated");
monitor();
}
/**
* 监控线程池情况
*/
public void monitor(){
log.info("正在工作的线程数:{}",getActiveCount());
log.info("当前存在的线程数:{}",getPoolSize());
log.info("历史最大的线程数:{}",getLargestPoolSize());
log.info("已提交的任务总数:{}",getTaskCount());
log.info("已完成的任务数:{}",getCompletedTaskCount());
log.info("队列中的任务数:{}",getQueue().size());
log.info("================线程池华丽分割线================");
}
}
- 监控方法monitor
- 重写beforeExecute方法,该方法每次任务执行前调用,在他内部调用一遍monitor方法,每当有任务执行的时候,输出一次线程池的情况,
- 接着重写afterExecute方法,该方法每次任务完成后调用,在它内部也调用一遍monitor方法,每当有任务完成的时候,输出一次线程池的情况,
- 最后重写terminated的方法。该方法在线程池关闭前调用,同样的,在它的内部也调用一遍monitor方法
- 当线程池关闭前一刻,我们可以了解到线程池最后的情况,至此整个带监控功能的线程池编写完成
自定义线程池中线程的名称的4种方式
Spring 框架提供的 CustomizableThreadFactory
private ThreadFactory springThreadFactory = new CustomizableThreadFactory("springThread-pool-");
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
10,
30,
5,
TimeUnit.MINUTES,
new ArrayBlockingQueue<Runnable>(1000),
springThreadFactory ); //给线程池中的线程自定义名称
Google guava工具类 提供的 ThreadFactoryBuilder ,使用链式方法创建。
private ThreadFactory guavaThreadFactory = new ThreadFactoryBuilder().setNameFormat("retryClient-pool-").build();
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
10,
30,
5,
TimeUnit.MINUTES,
new ArrayBlockingQueue<Runnable>(1000),
guavaThreadFactory ); //给线程池中的线程自定义名称
Apache commons-lang3提供的 BasicThreadFactory
private ThreadFactory basicThreadFactory = new BasicThreadFactory.Builder().namingPattern("basicThreadFactory-").build();
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
10,
30,
5,
TimeUnit.MINUTES,
new ArrayBlockingQueue<Runnable>(1000),
basicThreadFactory ); //给线程池中的线程自定义名称
自定义ThreadFactory
public class NamesThreadFactory implements ThreadFactory{
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
public NamesThreadFactory(String name) {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
if (null == name || name.isEmpty()) {
name = "pool";
}
namePrefix = name + "-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
使用方式
private NamesThreadFactory namesThreadFactory = new NamesThreadFactory("namesThread-");
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
10,
30,
5,
TimeUnit.MINUTES,
new ArrayBlockingQueue<Runnable>(1000),
namesThreadFactory); //给线程池中的线程自定义名称
测试我们的监控线程池的功能
定义Task
package com.kang.mongodb.pool;
import lombok.extern.slf4j.Slf4j;
/**
* @Author Emperor Kang
* @ClassName Task
* @Description 制定一个任务task,实现runnable接口,定义一个int类型的变量timeout,表示任务执行时长,重载构造方法用于初始化timeout,任务内容是使当前线程休眠,以此来模拟任务执行时长。
* @Date 2023/8/7 14:07
* @Version 1.0
* @Motto 让营地比你来时更干净
*/
@Slf4j
public class Task implements Runnable{
/**
* 执行时间
*/
private int timeout;
public Task(int timeout) {
this.timeout = timeout;
}
@Override
public void run() {
try {
//打印自定义线程名
log.info("当前线程名称:{}",Thread.currentThread().getName());
//使当前线程休眠指定时间
Thread.sleep(timeout * 1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 这里使用了spring的自定义线程名称的方式
编写main方法
- 接下来使用带监控功能的线程池,执行该任务。首先创建一个带监控功能的限制值,并指定核心线程数为1,最大线程数为三,空闲线程存活时间为0秒,任务队列采用linkedblockingqueue,并指定队列长度为二,接着使用for循环。从5~1提交5个任务,创建任务并指定任务执行时长为I,接着提交任务,每隔500毫秒提交一个,sleep方法有异常抛出使用try—catch将其捕获。
- 任务提交完以后,我们使主线程休眠6秒钟,这样做的目的在于,想在关闭线程池之前获取一次线程池的情况,最后写上finally代码块,在finally代码块中调用shoudown方法关闭线程池。至此main方法编写完成,
package com.kang.mongodb.pool;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
/**
* @Author Emperor Kang
* @ClassName MainTest
* @Description TODO
* @Date 2023/8/7 14:11
* @Version 1.0
* @Motto 让营地比你来时更干净
*/
public class MainTest {
public static void main(String[] args) {
// 自定义线程池名称
ThreadFactory threadFactory = new CustomizableThreadFactory("bigdata-thread-pool-");
// 创建带监控的线程池
MonitorThreadPool monitorThreadPool = new MonitorThreadPool(1, 3, 0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(2),threadFactory);
try {
// 提交多个任务
for (int i = 10; i > 0; i--) {
// 创建任务
Task task = new Task(i);
// 提交任务
monitorThreadPool.submit(task);
// 每隔500毫秒提交一个
Thread.sleep(500);
}
// 使主线程休眠6秒钟
Thread.sleep(6000);
// 关闭线程池之前获取一次线程池情况
monitorThreadPool.monitor();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 关闭线程池
monitorThreadPool.shutdown();
}
}
}
执行程序观察输出
MonitorThreadPool - beforeExecute
MonitorThreadPool - 正在工作的线程数:1
MonitorThreadPool - 当前存在的线程数:1
MonitorThreadPool - 历史最大的线程数:1
MonitorThreadPool - 已提交的任务总数:1
MonitorThreadPool - 已完成的任务数:0
MonitorThreadPool - 队列中的任务数:0
MonitorThreadPool - ================线程池华丽分割线================
Task - 当前线程名称:bigdata-thread-pool-1
MonitorThreadPool - beforeExecute
MonitorThreadPool - 正在工作的线程数:2
MonitorThreadPool - 当前存在的线程数:2
MonitorThreadPool - 历史最大的线程数:2
MonitorThreadPool - 已提交的任务总数:4
MonitorThreadPool - 已完成的任务数:0
MonitorThreadPool - 队列中的任务数:2
MonitorThreadPool - ================线程池华丽分割线================
Task - 当前线程名称:bigdata-thread-pool-2
MonitorThreadPool - beforeExecute
MonitorThreadPool - 正在工作的线程数:3
MonitorThreadPool - 当前存在的线程数:3
MonitorThreadPool - 历史最大的线程数:3
MonitorThreadPool - 已提交的任务总数:5
MonitorThreadPool - 已完成的任务数:0
MonitorThreadPool - 队列中的任务数:2
MonitorThreadPool - ================线程池华丽分割线================
14:28:24.815 [bigdata-thread-pool-3] INFO com.kang.mongodb.pool.Task - 当前线程名称:bigdata-thread-pool-3
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@14bf9759 rejected from com.kang.mongodb.pool.MonitorThreadPool@553f17c[Running, pool size = 3, active threads = 3, queued tasks = 2, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
at com.kang.mongodb.pool.MainTest.main(MainTest.java:29)
MonitorThreadPool - afterExecute
MonitorThreadPool - 正在工作的线程数:3
MonitorThreadPool - 当前存在的线程数:3
MonitorThreadPool - 历史最大的线程数:3
MonitorThreadPool - 已提交的任务总数:5
MonitorThreadPool - 已完成的任务数:0
MonitorThreadPool - 队列中的任务数:2
MonitorThreadPool - ================线程池华丽分割线================
MonitorThreadPool - beforeExecute
MonitorThreadPool - 正在工作的线程数:3
MonitorThreadPool - 当前存在的线程数:3
MonitorThreadPool - 历史最大的线程数:3
MonitorThreadPool - 已提交的任务总数:5
MonitorThreadPool - 已完成的任务数:1
MonitorThreadPool - 队列中的任务数:1
MonitorThreadPool - ================线程池华丽分割线================
Task - 当前线程名称:bigdata-thread-pool-3
MonitorThreadPool - afterExecute
MonitorThreadPool - 正在工作的线程数:3
MonitorThreadPool - 当前存在的线程数:3
MonitorThreadPool - 历史最大的线程数:3
MonitorThreadPool - 已提交的任务总数:5
MonitorThreadPool - 已完成的任务数:1
MonitorThreadPool - 队列中的任务数:1
MonitorThreadPool - ================线程池华丽分割线================
MonitorThreadPool - beforeExecute
MonitorThreadPool - 正在工作的线程数:3
MonitorThreadPool - 当前存在的线程数:3
MonitorThreadPool - 历史最大的线程数:3
MonitorThreadPool - 已提交的任务总数:5
MonitorThreadPool - 已完成的任务数:2
MonitorThreadPool - 队列中的任务数:0
MonitorThreadPool - ================线程池华丽分割线================
Task - 当前线程名称:bigdata-thread-pool-2
MonitorThreadPool - afterExecute
MonitorThreadPool - 正在工作的线程数:3
MonitorThreadPool - 当前存在的线程数:3
MonitorThreadPool - 历史最大的线程数:3
MonitorThreadPool - 已提交的任务总数:5
MonitorThreadPool - 已完成的任务数:2
MonitorThreadPool - 队列中的任务数:0
MonitorThreadPool - ================线程池华丽分割线================
MonitorThreadPool - afterExecute
MonitorThreadPool - 正在工作的线程数:2
MonitorThreadPool - 当前存在的线程数:2
MonitorThreadPool - 历史最大的线程数:3
MonitorThreadPool - 已提交的任务总数:5
MonitorThreadPool - 已完成的任务数:3
MonitorThreadPool - 队列中的任务数:0
MonitorThreadPool - ================线程池华丽分割线================
MonitorThreadPool - afterExecute
MonitorThreadPool - 正在工作的线程数:1
MonitorThreadPool - 当前存在的线程数:1
MonitorThreadPool - 历史最大的线程数:3
MonitorThreadPool - 已提交的任务总数:5
MonitorThreadPool - 已完成的任务数:4
MonitorThreadPool - 队列中的任务数:0
MonitorThreadPool - ================线程池华丽分割线================
MonitorThreadPool - terminated
MonitorThreadPool - 正在工作的线程数:0
MonitorThreadPool - 当前存在的线程数:0
MonitorThreadPool - 历史最大的线程数:3
MonitorThreadPool - 已提交的任务总数:5
MonitorThreadPool - 已完成的任务数:5
MonitorThreadPool - 队列中的任务数:0
MonitorThreadPool - ================线程池华丽分割线================