【技术应用】java实现动态线程池ThreadPoolExecutor

一、前言

在平时工作中经常使用到java线程池,用于提高程序的执行效率,同时我们使用的很多依赖组件也都用到了线程池,可以说线程池在开发中应用的非常广,我们经常涉及到的场景是这样的,有三组线程池接收数据+处理数据+发送数据
线程池虽然好用但是也会涉及到性能问题或者说耗尽CPU的问题,反而导致程序运作慢,线程池大小的设置一直都是一个头疼的问题,不同的环境需要设置不同的参数才能达到最优的状态,一般周围同事在开发中,线程池的参数都是固定写在代码中,这样导致每次修改参数都得重新编译程序,好一点的会把参数写到配置文件中,但是这样也会导致每次修改参数导致服务重启的问题,为了解决这个问题,我们想着开发一个公共组件可动态的修改线程池的参数,避免上述问题;
想要实现上述功能,首先了解线程池的功能、原理和提供的方法有哪些?

二、ThreadPoolExecutor方法

1)corePoolSize:核心线程数
线程池里的线程有两种,一种叫核心线程,另一种叫做非核心线程。
2)maximumPoolSize:最大线程数
也就是核心线程加非核心线程的总数。
3)keepAliveTime:线程允许的空闲时间
如果超过这个时间,线程将被终止。
默认情况下指的是非核心线程的空闲时间,但是如果设置了executor.allowCoreThreadTimeOut(true);那么表达的是核心线程或非核心线程允许的空闲时间。
4)TimeUnitkeepAliveTime的时间单位
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),不用自己开发,并且成熟度比较高。

==多存粮,满满的安全感=
在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dylan~~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值