为什么要使用线程池
- 在实际的项目中,使用多线程时严禁直接new线程,必须要结合线程池来维护和创建线程。
- 因为频繁的开启线程或者停止线程,线程需要重新被 cpu 从就绪到运行状态调度,需要发生 cpu 的上下文切换,效率非常低。
- 线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。
- 如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
线程池有哪些作用?
线程池的作用在于统一维护管理我们项目中的线程。 核心点就是复用机制。
- 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。
- 提高响应速度:任务到达时,无需等待线程创建即可立即执行。
- 提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。
- 提供更多更强大就允许任务延期执行或定期执行。
线程池的创建方式
Executors.newCachedThreadPool(); 可缓存线程池
Executors.newFixedThreadPool();可定长度 限制最大线程数
Executors.newScheduledThreadPool() ; 可定时
Executors.newSingleThreadExecutor(); 单例
上面的创建方式底层采用的是无界的LinkedBlockingQueue队列,可能会有内存溢出的的问题。
真实开发中底层都是基于 ThreadPoolExecutor 构造函数封装使用线程池。
Executors.newFixedThreadPool()使用的ThreadPoolExecutor参数
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
ThreadPoolExecutor核心参数
- corePoolSize:核心线程数量(一直正在保持运行的线程)
- maximumPoolSize:最大线程数(线程池允许创建的最大线程数)。满足最大线程数>=核心线程数
如当核心线程数量是为2(一直在运行状态),最大线程数是4(是当我们队列容量满了 就触发创建线程) - keepAliveTime:超出corePoolSize后创建的线程(最大线程数)的存活时间。
- unit:keepAliveTime的时间单位。
- workQueue:任务队列,用于保存待执行的任务。
详情可以看这篇文章:阻塞队列BlockingQueue - threadFactory:线程池内部创建线程所用的工厂。
- handler:任务无法执行时的处理器。
线程池的运行流程
- 当线程数小于核心线程数时,创建线程。
- 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
- 当线程数大于等于核心线程数,且任务队列已满
1.若线程数小于最大线程数,创建线程
2.若线程数等于最大线程数,抛出异常,拒绝任务
线程池内部的五种状态
- RUNNING
状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。
状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0! - SHUTDOWN
状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。 - STOP
状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。 - TIDYING
状态说明:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。
当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。 - TERMINATED
状态说明:线程池彻底终止,就变成TERMINATED状态。
状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。
实战使用
通常会创建一个配置类进行线程池参数配置,也可以将线程池封装为一个工具类,使用时调用。
代码示例:
import com.lc.soft.config.info.TimerThreadPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author qf
* @since 2024/08/31
*/
@Configuration
@EnableAsync
public class ThreadPoolConfig {
@Autowired
private TimerThreadPoolConfig timerThreadPoolConfig;
/**
* 定时器线程池
*
* @return
*/
@Bean(name = "timerExecutor")
public ThreadPoolTaskExecutor timerThreadPoolExecutor() {
return getAsyncTaskExecutor("Timer-Executor-", timerThreadPoolConfig.getCoreSize(),
timerThreadPoolConfig.getMaxSize(), timerThreadPoolConfig.getQueueCapacity(),
timerThreadPoolConfig.getKeepAliveSeconds(), null);
}
...
//不同业务建议配置不同的线程池
/**
* 统一异步线程池
* @param threadNamePrefix
* @param corePoolSize
* @param maxPoolSize
* @param queueCapacity
* @param keepAliveSeconds
* @param rejectedExecutionHandler 拒接策略 没有填null
* @return
*/
private ThreadPoolTaskExecutor getAsyncTaskExecutor(String threadNamePrefix, int corePoolSize, int maxPoolSize, int queueCapacity, int keepAliveSeconds, RejectedExecutionHandler rejectedExecutionHandler) {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(corePoolSize);
taskExecutor.setMaxPoolSize(maxPoolSize);
taskExecutor.setQueueCapacity(queueCapacity);
taskExecutor.setThreadPriority(Thread.MAX_PRIORITY);//线程优先级
taskExecutor.setDaemon(false);//是否为守护线程
taskExecutor.setKeepAliveSeconds(keepAliveSeconds);
taskExecutor.setThreadNamePrefix(threadNamePrefix);
taskExecutor.setRejectedExecutionHandler(rejectedExecutionHandler);
taskExecutor.initialize();//线程池初始化
return taskExecutor;
}
}
线程池参数配置类
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author qf
* @since 2024/08/31
*/
@Component
@ConfigurationProperties(prefix = "thread-pool-config.timer")
@Data
public class TimerThreadPoolConfig {
private Integer coreSize;
private Integer maxSize;
private Integer queueCapacity;
private Integer keepAliveSeconds;
}
application.yml
thread-pool-config:
timer:
core-size: 8
max-size: 16
queue-capacity: 64
keep-alive-seconds: 180