线程池监控

如何监控线程池

线程池两个点需要监控

第一点:线程的变化情况

在这里插入图片描述

第二点:任务的变化

在这里插入图片描述

用来监控线程变化的方法

方法描述监控方面
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 - ================线程池华丽分割线================
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值