springboot线程池统一线程定义和使用

1.我们都知道spring只是为我们简单的处理线程池,每次用到线程总会new 一个新的线程,效率不高,所以我们需要自定义一个线程池。

2.自定义线程池有两种方法,第一种自定义线程池然后使用自己的自定义的,第二种重写spring默认的线程池,然后使用自己重写过的线程池

一:重写spring默认的线程池


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Primary;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.ObjectUtils;
import org.springframework.util.concurrent.ListenableFuture;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.OperatingSystemMXBean;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author yzd
 * @Description: 显示线程池信息
 * <p>
 * 显示线程执行情况:任务总数、已完成数、活跃线程数,队列大小信息
 * </p>
 * @create 2021-04-29 9:28
 **/

@Primary
public class VisibleThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    private void showThreadPoolInfo(String prefix) {
        ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();

        if (ObjectUtils.isEmpty(threadPoolExecutor)) {
            return;
        }

        log.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
                this.getThreadNamePrefix(),
                prefix,
                threadPoolExecutor.getTaskCount(),
                threadPoolExecutor.getCompletedTaskCount(),
                threadPoolExecutor.getActiveCount(),
                threadPoolExecutor.getQueue().size());

        this.showMemoryInfo();
    }

    /**
     * execute 没有返回值 异步
     * submit有返回值,所以需要返回值的时候必须使用submit
     * 如果不对返回值Future调用get()方法,都会吃掉异常
     *
     * @param task task
     */
    @Override
    public void execute(Runnable task) {
        showThreadPoolInfo("1. do execute");
        super.execute(task);
    }

    @Override
    public void execute(Runnable task, long startTimeout) {
        showThreadPoolInfo("2. do execute timeout");
        super.execute(task, startTimeout);
    }

    @Override
    public Future<?> submit(Runnable task) {
        showThreadPoolInfo("1. do submit");
        return super.submit(task);
    }

    @Override
    public <T> Future<T> submit(Callable<T> task) {
        showThreadPoolInfo("2. do submit");
        return super.submit(task);
    }

    @Override
    public ListenableFuture<?> submitListenable(Runnable task) {
        showThreadPoolInfo("1. do submitListenable");
        return super.submitListenable(task);
    }

    @Override
    public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
        showThreadPoolInfo("2. do submitListenable");
        return super.submitListenable(task);
    }

    /**
     * Java 虚拟机的内存系统
     * <p>
     * init约等于xms的值,max约等于xmx的值
     * used是已经被使用的内存大小,committed是当前可使用的内存大小(包括已使用的)
     * committed >= used。committed不足时jvm向系统申请,若超过max则发生OutOfMemoryError错误
     * 机器内存小于192M,堆内存最大为二分之一
     * 机器内存大于等于1024M,堆内存最大为四分之一
     * JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;
     * JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4。
     * 默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;
     * 空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。
     * 因此服务器一般设置-Xms、-Xmx相等以避免在每次GC后调整堆的大小。
     * </p>
     */
    private void showMemoryInfo() {
        MemoryMXBean mem = ManagementFactory.getMemoryMXBean();
        MemoryUsage heap = mem.getHeapMemoryUsage();
        log.info("Heap committed:" + heap.getCommitted() / 1024 / 1024
                + "MB, init:" + heap.getInit() / 1024 / 1024
                + "MB, max:" + heap.getMax() / 1024 / 1024
                + "MB, used:" + heap.getUsed() / 1024 / 1024 + "MB");
    }

    /**
     * Java 虚拟机在其上运行的操作系统
     */
    private void showSystem() {
        OperatingSystemMXBean op = ManagementFactory.getOperatingSystemMXBean();
        log.info("架构:[{}],处理器数:[{}],操作系统:[{}],系统版本:[{}]", op.getArch(), op.getAvailableProcessors(), op.getName(), op.getVersion());
    }

}

二:自定义线程池配置类


import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

import javax.annotation.PreDestroy;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * ExecutorConfig
 *
 * @author : YZD
 * @date : 2023/3/7 0007 21:18
 */
@Configuration
@EnableAsync
@Slf4j
@EnableScheduling
public class ExecutorConfig implements SchedulingConfigurer {

	private static final ThreadPoolTaskExecutor EXECUTOR = new VisibleThreadPoolTaskExecutor();

	/**
	 * 功能描述:  线程池
	 *
	 * @return : org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
	 * @author : yzd e-mail: 121665820@qq.com
	 * @create : 2023/2/11 16:04
	 */
	@Bean
	@Primary
	public ThreadPoolTaskExecutor asyncServiceExecutor() {
		log.info("start asyncService Executor");
		//使用visibleThreadPoolTaskExecutor

		// 通过Runtime方法来获取当前服务器cpu内核,根据cpu内核来创建核心线程数和最大线程数
		int availableProcessors = Runtime.getRuntime().availableProcessors();
		/**
		 * 配置线程个数
		 如果是CPU密集型任务,那么线程池的线程个数应该尽量少一些,一般为CPU的个数+1条线程(大量计算)
		 如果是IO密集型任务,那么线程池的线程可以放的很大,如2*CPU的个数(IO操作)
		 */
		EXECUTOR.setCorePoolSize(availableProcessors + 1);
		// 允许线程池超时
		EXECUTOR.setAllowCoreThreadTimeOut(true);
		//配置最大线程数
		EXECUTOR.setMaxPoolSize(availableProcessors * 4);
		// 空闲存活时间
		EXECUTOR.setKeepAliveSeconds(60);
		// 设置 等待终止秒数
		EXECUTOR.setAwaitTerminationSeconds(60);
		//配置队列大小
		EXECUTOR.setQueueCapacity(availableProcessors * 100);
		//配置线程池中的线程的名称前缀
		EXECUTOR.setThreadNamePrefix("async-thread-pool-");
		// 等待所有任务结束再关闭线程池
		EXECUTOR.setWaitForTasksToCompleteOnShutdown(true);
		// rejection-policy:当pool已经达到max size的时候,如何处理新任务
		// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
		EXECUTOR.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
		//执行初始化
		EXECUTOR.initialize();
		return EXECUTOR;
	}

	/**
	 * 创建一个定长线程池,支持定时及周期性任务执行
	 */
	@Bean
	public ThreadPoolTaskScheduler scheduledThreadPoolExecutor() {
		log.info("start Scheduled Executor");
		ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
		// 大于等于 任务数量
		executor.setPoolSize(10);
		executor.setThreadNamePrefix("scheduled-thread");
		// 等待时长
		executor.setAwaitTerminationSeconds(60);
		// 调度器shutdown被调用时等待当前被调度的任务完成
		executor.setWaitForTasksToCompleteOnShutdown(true);
		//设置饱和策略
		//CallerRunsPolicy:线程池的饱和策略之一,当线程池使用饱和后,直接使用调用者所在的线程来执行任务;如果执行程序已关闭,则会丢弃该任务
		executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
		executor.initialize();
		return executor;
	}

	/**
	 * 功能描述:  配置 @Scheduled 定时器所使用的线程池
	 * 配置任务注册器:ScheduledTaskRegistrar 的任务调度器
	 *
	 * @param scheduledTaskRegistrar scheduledTaskRegistrar
	 * @return : void
	 * "@Scheduled" 默认是单线程执行的,所以在需要的时候,我们可以设置一个线程池去执行定时任务。
	 * @author : yzd e-mail: 121665820@qq.com
	 * @create : 2023/2/11 16:30
	 */
	@Override
	public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
		//可配置两种类型:TaskScheduler、ScheduledExecutorService
		// scheduledTaskRegistrar.setScheduler(scheduledThreadPoolExecutor());
		//只可配置一种类型:taskScheduler
		scheduledTaskRegistrar.setTaskScheduler(scheduledThreadPoolExecutor());
	}

	/**
	 * 优雅关闭线程池
	 */
	@PreDestroy
	private void shutdownGracefully() {
		log.error("ThreadPool close");
	}

}

三:使用多线程


@Slf4j
@Component
public class LogAspect {
    @Autowired
    private ThreadPoolTaskExecutor asyncServiceExecutor;

    private void controllerAspect() {
          // 异步 CompletableFuture
        asyncServiceExecutor.execute(() -> {
                    // 日志存储
        );
    }
}

四、使用定时任务


import org.springblade.modules.article.service.IArticleService;
import org.springblade.modules.im.utils.MsgUtils;
import org.springblade.modules.monitor.service.IMonitorLifeSignService;
import org.springblade.modules.order.req.IServiceItemInfoService;
import org.springblade.modules.plan.service.IPlanNoticeService;
import org.springblade.modules.plan.service.IPlanService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @Title: StartInit
 * @author: yzd e-mail: 121665820@qq.com
 * @date: 2023/2/13 17:22
 * @ClassName: StartInit
 * @Description: 项目初始化完毕后, 调用方法,提供服务
 * 采用 定时 线程池 , 执行 定时 任务
 * 每个 任务 把 执行 时间 错开 ,  以免 多任务 执行 失败
 * scheduleAtFixedRate,指定间隔时间执行一次任务,间隔时间为前一次执行开始到下次任务开始时间
 * scheduleWithFixedDelay,指定间隔时间执行一次任务,间隔时间为前一次任务完成到下一次开始时间
 * schedule(Runnable task, Date stateTime),在指定时间执行一次定时任务
 * schedule(Runnable task, Trigger trigger),动态创建指定表达式cron的定时任务
 */
@Component
public class StartTaskConfig implements CommandLineRunner {

	@Autowired
	private ThreadPoolTaskScheduler threadPoolTaskScheduler;

	@Autowired
	private IPlanService planService;


	/**
	 * 功能描述:  定时任务1
	 * 扫描所有的 Channel,关闭失效的Channel, 每 24 小时
	 *
	 * @return : void
	 * @author : yzd e-mail: 121665820@qq.com
	 * @create : 2023/3/16 14:33
	 */
	@Scheduled(fixedDelay = 24 * 60 * 60 * 1000L, initialDelay = 60 * 60 * 1000L)
	public void scanNotActiveChannel() {
		MsgUtils.scanNotActiveChannel();
	}

	@Override
	public void run(String... args) {
		// 定时任务2:  方案  自启动, 每 65 分钟  执行
		threadPoolTaskScheduler.scheduleWithFixedDelay(planService::autoStartPlan, 65 * 60 * 1000L);
	
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阳宗德

您的鼓励是我进步的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值