开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第14天,点击查看活动详情
8.1线程池
- 自定义线程池
运用了策略模式:通过封装接口面向对象的思想可以自定义拒绝策略
8.2 ThreadPoolExecutor
1.线程池状态
ThreadPoolExecutor 使用 int 的高 3 位来表示线程池状态,低 29 位表示线程数量
这些信息存储在一个原子变量 ctl 中,目的是将线程池状态与线程个数合二为一,这样就可以用一次 cas 原子操作 进行赋值
2.构造方法
- corePoolSize 核心线程数目 (最多保留的线程数)
- maximumPoolSize 最大线程数目
- keepAliveTime 生存时间 - 针对救急线程
- unit 时间单位 - 针对救急线程
- workQueue 阻塞队列
- threadFactory 线程工厂 - 可以为线程创建时起个好名字
- handler 拒绝策略
救急线程:当当前需要执行任务数大于核心线程数 + 阻塞队列大小 时,将任务分发给救急线程进行处理,当救急线程全部派上用场却还是无法满足需求时就会执行拒绝策略
jdk提供了4种拒绝策略
- AbortPolicy 让调用者抛出 RejectedExecutionException 异常,这是默认策略
- CallerRunsPolicy 让调用者运行任务
- DiscardPolicy 放弃本次任务
- DiscardOldestPolicy 放弃队列中最早的任务,本任务取而代之
其他框架给出了额外的拒绝策略
- Dubbo 的实现,在抛出 RejectedExecutionException 异常之前会记录日志,并 dump 线程栈信息,方 便定位问题
- Netty 的实现,是创建一个新线程来执行任务
- ActiveMQ 的实现,带超时等待(60s)尝试放入队列,类似我们之前自定义的拒绝策略
- PinPoint 的实现,它使用了一个拒绝策略链,会逐一尝试策略链中每种拒绝策略
根据这个构造方法,JDK Executors 类中提供了众多工厂方法来创建各种用途的线程池
3.newFixedThreadPool(创建固定大小的线程池)
特点
- 核心线程数 == 最大线程数(没有救急线程被创建),因此也无需超时时间
- 阻塞队列是无界的,可以放任意数量的任务
评价 适用于任务量已知,相对耗时的任务
举个栗子
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2, new ThreadFactory() {
private AtomicInteger i = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r,"xksdspoll" + i.getAndIncrement());
}
});
pool.execute(()->{
System.out.println(Thread.currentThread().getName() + "1");
});
pool.execute(()->{
System.out.println(Thread.currentThread().getName() +"2");
});
pool.execute(()->{
System.out.println(Thread.currentThread().getName() +"3");
});
}