概念
- 线程池由任务队列和工作线程组成。线程执行完任务后不会退出,会继续执行新任务或阻塞等待。
- 线程池可以管理并重用线程,控制最大并发数,避免频发创建线程的开销。
- 任务过多时,通过排队避免创建过多线程,减少系统资源消耗和竞争,确保任务有序执行
- 隔离线程环境,避免不同业务的线程相互影响
ThreadPoolExecutor
Executor 任务执行接口
public interface Executor {
// 执行任务
void execute(Runnable command);
}
ExecutorService 接口
public interface ExecutorService extends Executor {
// 表示提交一个任务,而不是执行
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
// 关闭任务执行,已提交的任务会继续执行,而不再接收新任务
void shutdown();
// 关闭任务执行, 终止已提交且未执行的任务,正在执行的任务调用interrupt方法,
返回已提交尚未执行的任务。
List<Runnable> shutdownNow();
}
ThreadPoolExecutor 线程池类
构造方法
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; // 拒绝策略
}
实现原理
1、 有效线程数小于corePoolSize,创建新线程执行任务
2、 线程数大于核心线程数,任务会添加到任务队列
3、 如果任务队列已满,线程数小于maxPoolSize,创建新线程执行任务
4、 线程数大于最大线程数,则执行拒绝策略
当线程数大于核心线程数时,并且无任务执行则会等待keepAliveTime时间,,线程最长等待时间后仍没有任务就会被终止
任务队列 BlockingQueue
BlockingQueue 为阻塞队列接口,入队或出队需要阻塞等待。常用于生产者消费者模式。
RejectedExecutionHandler 拒绝策略
当队列有界已满且线程数大于maxPoolSize时会执行拒绝策略
ThreadPoolExecutor.AbortPolicy // 默认策略,抛出异常
ThreadPoolExecutor.CallerRunsPolicy // 任务提交线程执行任务,非线程池
ThreadPoolExecutor.DiscardOldestPolicy // 丢弃等待时间最长的任务,自己入队
ThreadPoolExecutor.DiscardPolicy // 忽略新任务
也可以自定义拒绝策略,实现RejectedExecutionHandler接口
Executors
Executors.newCachedThreadPool 高度可甚多的线程池
Executors.newFixedThreadPool 固定线程数的线程池
Executors.newScheduledThreadPool 支持定时和周期性任务执行
Executors.newSingleThreadExecutor 单线程的线程池,串行(顺序)执行任务
Executors.newWorkStealingPool
线程池的使用
- 应根据业务场景设置合理的线程数
- 为线程池提供有意义的名称
- 拒绝策略应考虑业务场景
- 线程隔离
- 创建线程池不允许使用Executors,而应通过ThreadPoolExecutor来创建, 这样可以明确线程池的运行规则,规避资源耗尽的风险