【技术应用】java实现动态线程池ThreadPoolExecutor
一、前言
在平时工作中经常使用到java线程池,用于提高程序的执行效率,同时我们使用的很多依赖组件也都用到了线程池,可以说线程池在开发中应用的非常广,我们经常涉及到的场景是这样的,有三组线程池
:接收数据
+处理数据
+发送数据
;
线程池虽然好用但是也会涉及到性能问题或者说耗尽CPU
的问题,反而导致程序运作慢,线程池大小的设置一直都是一个头疼的问题,不同的环境需要设置不同的参数才能达到最优的状态,一般周围同事在开发中,线程池的参数都是固定写在代码中,这样导致每次修改参数都得重新编译程序,好一点的会把参数写到配置文件中,但是这样也会导致每次修改参数导致服务重启的问题,为了解决这个问题,我们想着开发一个公共组件可动态的修改线程池的参数,避免上述问题;
想要实现上述功能,首先了解线程池的功能、原理和提供的方法有哪些?
二、ThreadPoolExecutor方法
1)corePoolSize
:核心线程数
线程池里的线程有两种,一种叫核心线程,另一种叫做非核心线程。
2)maximumPoolSize
:最大线程数
也就是核心线程加非核心线程的总数。
3)keepAliveTime
:线程允许的空闲时间
如果超过这个时间,线程将被终止。
默认情况下指的是非核心线程的空闲时间,但是如果设置了executor.allowCoreThreadTimeOut(true)
;那么表达的是核心线程或非核心线程允许的空闲时间。
4)TimeUnit
:keepAliveTime
的时间单位
5)workQueue
:存储等待执行的任务,传入BlockingQueue
6)threadFactory
:用于创建线程的线程工厂
默认情况下使用的是defaultThreadFactory
。此外还有privilegedThreadFactory
。
其中PrivilegedThreadFactory继承了DefaultThreadFactory,可以让运行在这个线程中的任务拥有和这个线程相同的访问控制和ClassLoader。实际项目中使用DefaultThreadFactory的场景更多一些。
7)rejectHandler
:拒绝任务的策略
如果workQueue满了,当前的线程池也没有空闲的线程,当时依然还有任务被提交进来,这个时候就需要使用拒绝策略去处理这个任务。
a、AbortPolicy
(默认):抛异常
b、CallerRusPolicy
:用调用者所在的线程执行任务
c、DiscardOldestPolicy
:丢弃队列中最靠前的任务
d、DiscardPolicy
:丢弃当前任务
操作类方法:
execute()
:提交任务,交给线程池执行
submit()
:提交任务,能够返回执行结果
shutdown()
:关闭线程池,等待任务都执行完
shutdownNow()
:关闭线程池,不等待任务执行完(很少使用)
监控类方法:
getTaskCount()
:返回线程池已执行和未执行的任务总数
getCompletedTaskCount()
:已完成的任务数量
getPoolSize()
:线程池当前的线程数量
getActiveCount()
:线程池中正在执行任务的线程数量
三、线程池状态转换
=其它线程池内容不再详细介绍,大家可以自行百度=
四、代码示例
1)动态线程池类
DynamicThreadPoolExecutor
类中的方法很多,这里只简单介绍一下对大家有用的方法,DynamicThreadPoolExecutor
是自定义的线程池类,实际还是通过ThreadPoolExecutor
线程池类实现的;
start()
方法用于初始化线程池ThreadPoolExecutor
和创建监控线程池
,初始化线程池不用多说,监控线程主要用于定时监控获取线程池中活跃线程数(threadPoolTaskExecutor.getActiveCount()
)和等待队列任务数
(threadPoolTaskExecutor.getQueue().size()
),当这两个值超过设置的阈值时,调用invokeAlarmPostProcessor()
方法,产生预警信息,可以通过发送邮件、短信提醒;
package thread.pool.core.executor;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.util.CollectionUtils;
import thread.pool.core.util.ResizableCapacityLinkedBlockIngQueue;
import thread.pool.core.util.ThrowableUtil;
import thread.pool.core.vo.ThreadPoolStatus;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 动态线程池类
* 这里实现了DisposableBean是为了在bean销毁时能够同时销毁掉线程池资源
* 实现InitializingBean 是为了在bean初始化阶段属性注入之后去创建线程池资源
* 过早地创建线程池 是为了防止其他bean创建完成的时候执行初始化任务依赖此线程池 保证了线程池在业务代码中正常使用
*/
@Data
public class DynamicThreadPoolExecutor implements DisposableBean, ApplicationContextAware, InitializingBean, Ordered {
private static final Logger logger = LoggerFactory.getLogger(DynamicThreadPoolExecutor.class);
private String taskDescribe;
private Integer corePoolSize = 2;
private Integer maxPoolSize = 20;
private Long keepAliveTime = 10L;
private TimeUnit unit;
private Integer dequeSize;
private String poolName;
private Integer beatTime;
private Integer alarmSize;
private ThreadPoolExecutor threadPoolTaskExecutor;
private ResizableCapacityLinkedBlockIngQueue<Runnable> blockIngQueue;
private volatile boolean STOP;
private final Object poolSizeMonitor;
// 检测线程池状态频率 毫秒单位
private volatile long checkFrequency = 1000;
// 拒绝策略 默认由主线程执行 然后告警通知程序员队列异常
private static RejectedExecutionHandler rejectedExecutionHandler =
new ThreadPoolExecutor.CallerRunsPolicy();
public DynamicThreadPoolExecutor() {
this.unit = TimeUnit.SECONDS;
this.dequeSize = 500;
this.poolName = "pool";
this.beatTime = 10;
this.threadPoolTaskExecutor = null;
this.blockIngQueue = null;
this.STOP = false;
this.alarmSize = 400;//告警阀值大于400 且默认时间内一直都是高于这个值 则认为应该关注下队列大小是否应该调整
this.poolSizeMonitor = new Object();// 对象初始化时创建锁
}
/**
* 资源要销毁
*/
public void destroy() {
this.shutDown();
logger.info(">>>>>>>>>线程池【{}】销毁成功", this.getPoolName());
}
public int getOrder() {
return 0;
}
/**
* bean成员属性构造注入之后
*/
public void afterPropertiesSet() {
this.start();
}
/**
* 线程池初始化
* 主要做了两件事 1 初始化线程池 2 初始化监控线程
*/
private void start() {
logger.info("======= thread-pool {} start init>>>>>>>>>>>>>>>>>>>>>", this.poolName);
this.blockIngQueue = new ResizableCapacityLinkedBlockIngQueue<>(this.dequeSize);
ThreadPoolExecutor executor = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, this.keepAliveTime,
this.unit, this.blockIngQueue,
new DefaultThreadFactory(this.poolName),
rejectedExecutionHandler);
logger.info("======= thread-pool {} init success >>>>>>>>>>>>>>>>>>>>>", this.poolName);
this.threadPoolTaskExecutor = executor;
logger.info(this + "\n");
// 新启线程负责检测线程池状态
(new Thread(() -> {
if (this.alarmSize == null) {
this.alarmSize = this.dequeSize;
}
while (!this.STOP) {
// 活跃线程数
int activeCount = this.threadPoolTaskExecutor.getActiveCount();
// 此时核心线程用尽 队列积压满了
if (activeCount > getCorePoolSize()) {
invokeAlarmPostProcessor();
}
// 队列中任务数量
int size = this.threadPoolTaskExecutor.getQueue().size();
if (size >= this.alarmSize) {
logger.warn("【线程池队列告警】线程池状态:{}", this);
// 这里是预留接口给程序员使用 这里可以将线程信息传递给使用者 由使用者决定如何告警(短信 or 邮件?)
invokeAlarmPostProcessor();
}
try {
Thread.sleep(checkFrequency);
} catch (InterruptedException e) {
logger.error("thread fail {}", ThrowableUtil.toString(e));
this.STOP = true;
}
}
})).start();
}
/**
* 执行告警处理器
* 模仿spring框架 提供给程序员扩展点 自定义实现业务逻辑
*/
private void invokeAlarmPostProcessor() {
ThreadPoolStatus threadPoolStatus = this.convertThreadPoolStatus();
// 获取所有的告警后置处理器bean
List<AlarmPostProcessor> alarmPostProcessors = obtainAlarmPostProcessorList();
if (alarmPostProcessors != null) {
for (AlarmPostProcessor postProcessor : alarmPostProcessors) {
postProcessor.postProcessor(threadPoolStatus);
}
}
}
public ThreadPoolExecutor getExecutor() {
return this.threadPoolTaskExecutor;
}
/**
* 刷新线程池状态
*/
public void refresh() {
synchronized (this.poolSizeMonitor) {
logger.info(">>>>>>>>>>>> 开始刷新线程池参数 >>>>>>>>>>>");
if (!this.threadPoolTaskExecutor.isShutdown() && !this.threadPoolTaskExecutor.isTerminated()) {
this.threadPoolTaskExecutor.setCorePoolSize(this.corePoolSize);
this.threadPoolTaskExecutor.setMaximumPoolSize(this.maxPoolSize);
this.updateDequeSize(this.dequeSize);
logger.info(">>>>>>>>>>>> 线程池参数刷新完毕 >>>>>>>>>>>");
logger.info(this + "\n");
this.STOP = false;
} else {
throw new RuntimeException("线程池状态异常 不可刷新");
}
}
}
protected void updateDequeSize(Integer dequeSize) {
if (this.blockIngQueue == null) {
throw new RuntimeException(" blockIngQueue is null ");
} else {
this.blockIngQueue.setCapacity(dequeSize);
}
}
public void execute(Runnable runnable) {
ThreadPoolExecutor threadPoolExecutor = this.getExecutor();
logger.info(">>>>>>>>> poolName:{},activeCount:{},queueTaskCount:{}", this.getPoolName(), threadPoolExecutor.getActiveCount(), threadPoolExecutor.getQueue().size());
threadPoolExecutor.execute(runnable);
}
public <E> Future<E> submit(Callable<E> callable) {
ThreadPoolExecutor executor = this.getExecutor();
return executor.submit(callable);
}
protected void shutDown() {
synchronized (this.poolSizeMonitor) {
ThreadPoolExecutor executor = this.getExecutor();
executor.shutdown();
if (executor.isShutdown()) {
executor = null;
}
}
}
public RejectedExecutionHandler getRejectedExecutionHandler() {
return rejectedExecutionHandler;
}
public void setRejectedExecutionHandler(RejectedExecutionHandler rejectedExecutionHandler) {
DynamicThreadPoolExecutor.rejectedExecutionHandler = rejectedExecutionHandler;
}
public String toString() {
return "线程池信息{ poolName='" + this.poolName + ", corePoolSize=" + this.corePoolSize + ", maxPoolSize=" + this.maxPoolSize + ", keepAliveTime=" + this.keepAliveTime + ", unit=" + this.unit + ", dequeSize=" + this.dequeSize + ",排队任务数=" + this.threadPoolTaskExecutor.getQueue().size() + ",活跃线程数=" + this.threadPoolTaskExecutor.getActiveCount() + '}';
}
private static ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
DynamicThreadPoolExecutor.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static <T> Map<String, T> getBeansOfType(Class<T> tClass) {
return applicationContext.getBeansOfType(tClass);
}
/**
* 转换线程状态信息
*
* @return 状态信息
*/
private ThreadPoolStatus convertThreadPoolStatus() {
ThreadPoolStatus threadPoolStatus = new ThreadPoolStatus();
threadPoolStatus.setCorePoolSize(this.getCorePoolSize());
threadPoolStatus.setPoolName(this.getPoolName());
threadPoolStatus.setActiveCount(this.threadPoolTaskExecutor.getActiveCount());
threadPoolStatus.setQueueCount(this.threadPoolTaskExecutor.getQueue().size());
threadPoolStatus.setMaxPoolSize(this.getMaxPoolSize());
threadPoolStatus.setAlarmSize(this.getAlarmSize());
threadPoolStatus.setTaskDescribe(this.getTaskDescribe());
threadPoolStatus.setDequeSize(this.getDequeSize());
return threadPoolStatus;
}
/**
* 获取spring中所有实现了AlarmPostProcessor接口的bean
*
* @return bean集合
*/
public static List<AlarmPostProcessor> obtainAlarmPostProcessorList() {
Map<String, AlarmPostProcessor> beansOfType = getBeansOfType(AlarmPostProcessor.class);
if (CollectionUtils.isEmpty(beansOfType)) {
return null;
}
return new ArrayList<>(beansOfType.values());
}
/**
* 自定义线程工厂
*/
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory(String poolName) {
SecurityManager s = System.getSecurityManager();
this.group = s != null ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
this.namePrefix = poolName + "-" + poolNumber.getAndIncrement() + "-thread-";
}
public Thread newThread(Runnable r) {
return new Thread(this.group, r, this.namePrefix + this.threadNumber.getAndIncrement(), 0L);
}
}
}
2)线程池参数修改
这个类主要对外提供线程池参数修改(update
)和查询接口(check
)
package thread.pool.core.biz;
import lombok.Data;
import thread.pool.core.executor.EmbedServerHolder;
import thread.pool.core.executor.DynamicThreadPoolExecutor;
import thread.pool.core.util.HttpResult;
import thread.pool.core.vo.PoolStatusVo;
import thread.pool.core.vo.UpdateVo;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
@Data
public class ExecutorBizImpl implements ExecutorBiz {
//
public HttpResult update(UpdateVo vo) {
String beanName = vo.getBeanName();
DynamicThreadPoolExecutor bean = EmbedServerHolder.getBean(beanName, DynamicThreadPoolExecutor.class);
if (vo.getCorePoolSize() != null) {
bean.setCorePoolSize(vo.getCorePoolSize());
}
if (vo.getMaxPoolSize() != null) {
bean.setMaxPoolSize(vo.getMaxPoolSize());
}
if (vo.getDequeSize() != null && vo.getDequeSize() > 0) {
bean.setDequeSize(vo.getDequeSize());
}
bean.refresh();
return HttpResult.ok();
}
//查询线程池参数
public HttpResult check() {
Map<String, DynamicThreadPoolExecutor> beansOfType = EmbedServerHolder.getBeansOfType(DynamicThreadPoolExecutor.class);
List<PoolStatusVo> list = new ArrayList<>();
beansOfType.forEach((k, v) -> {
PoolStatusVo poolStatusVo = new PoolStatusVo();
ThreadPoolExecutor executor = v.getExecutor();
poolStatusVo.setTaskCount(executor.getQueue().size());
poolStatusVo.setCorePoolSize(v.getCorePoolSize());
poolStatusVo.setMaxPoolSize(v.getMaxPoolSize());
poolStatusVo.setDequeSize(v.getDequeSize());
poolStatusVo.setPoolName(v.getPoolName());
poolStatusVo.setBeatTime(v.getBeatTime());
list.add(poolStatusVo);
});
return HttpResult.ok(list);
}
}
五、总结
示例仅供大家参考,实际应用中可以使用开源的动态可监控线程池框架(DynamicTp),不用自己开发,并且成熟度比较高。
==多存粮,满满的安全感=