目录
基础知识: 线程和进程
基础概念
- 线程:是CPU调度的最小单位
- 进程:是资源分配的最小单位
- 进程之间是相互独立的,每个进程都有自己的运行空间,不允许相互窥探;线程存在于进程内,共享进行的内存
线程的状态
线程池
线程池的优势:
(1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
(2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
(3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
)
- corePoolSize(必需):核心线程数。默认情况下,核心线程会一直存活,但是当将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
- maximumPoolSize(必需):线程池所能容纳的最大线程数。当活跃线程数达到该数值后,后续的新任务将会阻塞。
- keepAliveTime(必需):线程闲置超时时长。如果超过该时长,非核心线程就会被回收。如果将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
- unit(必需):指定 keepAliveTime 参数的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。
- workQueue(必需):任务队列。通过线程池的 execute() 方法提交的 Runnable 对象将存储在该参数中。其采用阻塞队列实现。
- threadFactory(可选):线程工厂。用于指定为线程池创建新线程的方式。
- handler(可选):拒绝策略。当达到最大线程数时需要执行的饱和策略。
线程池可参考:Java 多线程:彻底搞懂线程池_java线程池-CSDN博客
任务进入顺序:
- 核心线程
- 大于核心线程,进入任务队列
- 开新线程
- 大于最大线程,拒绝策略
为什么任务已经堆积很多了,队列满了,才开新线程处理任务?而不是核心线程满了之后,就开新线程执行任务?
答:降低资源消耗,防止频繁创建线程。
线程池的实际配置
@Bean(name = "scheduleThreadPool")
public Executor scheduleThreadPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// Core thread pool size
executor.setCorePoolSize(5);
// Max thread pool size
executor.setMaxPoolSize(10);
// Thread pool keep-alive time
executor.setKeepAliveSeconds(60);
// Queue capacity
executor.setQueueCapacity(25);
// Thread pool factory
executor.setThreadNamePrefix("Schedule-Thread-");
// Rejection policy
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// Initialize the executor
executor.initialize();
return executor;
}
以上参数配置仅供参考,可以根据实际调整。
1.线程名前缀:可用于日志排查
2.核心级线程:
CPU密集型:CPU核数+1
IO密集型: CPU核数/(1-阻塞系数)
3.拒绝策略:根据业务场景进行选择。
最佳实践:动态线程池,参考美团:Java线程池实现原理及其在美团业务中的实践 - 美团技术团队
可以考虑将这几个核心参数放到线上配置,实时监控,而不是写死到代码。