文章目录
面试官神技:“如来神掌”
请你说一下线程池的原理和使用?
提示:以下是本篇功法,记得收藏关注
见招拆招:简述概念以及优点
优点:
- 降低创建线程和销毁线程的性能开销。
- 提高响应速度,当有新任务需要执行是不需要等待线程创建就可以 立马执行。
- 合理的设置线程池大小可以避免因为线程数过多消耗CPU资源。
见龙在田:究其结构与使用规范
1.uml类图结构:

①Executor:可以看到最顶层是 Executor 的接口。这个接口很简单,只有一个 execute 方法。此接口的目的是为了把任务提交和任务执行解耦。
②ExecutorService:这还是一个接口,继承自 Executor,它扩展了 Executor 接口,定义了更多线程池相关的操作。
③AbstractExecutorService:提供了 ExecutorService 的部分默认实现。
④ThreadPoolExecutor:实际上我们使用的线程池的实现是 ThreadPoolExecutor。它实现了线程池工作的完整机制。也是我们接下来分析的重点对象。
⑤ForkJoinPool:和ThreadPoolExecutor都继承自AbstractExecutorService,适合用于分而治之,递归计算的算法
⑥ScheduledExecutorService:这个接口扩展了ExecutorService,定义个延迟执行和周期性执行任务的方法。
⑦ScheduledThreadPoolExecutor:此接口则是在继承 ThreadPoolExecutor 的基础上实现 ScheduledExecutorService 接口,提供定时和周期执行任务的特性。
2.创建线程池的主要使用

通过阿里的开发手册我们看到因为允许的阻塞队列的长度为Integer.MAX_VALUE,所以会出现OOM的问题,我们还是使用ThreadPoolExecutor构造方法进行线程池的创建。
public ThreadPoolExecutor(int corePoolSize, //核心线程数量
int maximumPoolSize, //最大线程数
long keepAliveTime, //超时时间,超出核心线程数量以外的线程空余存活时间
TimeUnit unit, //存活时间单位
BlockingQueue<Runnable> workQueue, //保存执行任务的队列
ThreadFactory threadFactory,//创建新线程使用的工厂
RejectedExecutionHandler handler //当任务无法执行的时候的处理方式)
注意:上面对其重要参数进行了简要讲解,源码系列发在下一篇
下面是线程池的基本创建示例:
// 创建线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE,
KEEP_ALIVE,
TimeUnit.SECONDS,
sPoolWorkQueue,
sThreadFactory);
// 向线程池提交任务
threadPool.execute(new Runnable() {
@Override
public void run() {
... // 线程执行的任务
}
});
// 关闭线程池
threadPool.shutdown(); // 设置线程池的状态为SHUTDOWN,然后中断所有没有正在执行任务的线程
threadPool.shutdownNow(); // 设置线程池的状态为 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表
corePoolSize:即线程池的核心线程数量,其实也是最小线程数量。不设置allowCoreThreadTimeOut 的情况下,核心线程数量范围内的线程一直存活。线程不会自行销毁,而是以挂起的状态返回到线程池,直到应用程序再次向线程池发出请求时,线程池里挂起的线程就会再度激活执行任务。
maximumPoolSize:即线程池的最大线程数量
keepAliveTime和unit:超出核心线程数后的存活时间和存活单位
workQueue:是一个阻塞的 queue,用来保存线程池要执行的所有任务。通常可以取下面三种类型:
1)ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小; 2)LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE; 3)SynchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。
ThreadFactory:我们一般用Executors.defaultThreadFactory()默认工厂,为什么要用工厂呢,其实就是规范了生成的Thread。避免调用new Thread创建,导致创建出来的Thread可能存在差异
handler:当队列和最大线程池都满了之后的拒绝策略。
1.AbortPolicy:直接抛出异常,默认策略; 2.CallerRunsPolicy:用调用者所在的线程来执行任务; 3、DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务; 4、DiscardPolicy:直接丢弃任务; 当然也可以根据应用场景实现 RejectedExecutionHandler 接口,自定义饱和策略,如记录 日志或持久化存储不能处理的任务
以上是对重要参数的基本使用,在核心参数下,线程池的性能和抗压能力会在参数的合理性下匹配,
线程池的工作流程

致命一击:常用线程池的使用
1.定长线程池(FixedThreadPool)
// 1. 创建定长线程池对象 & 设置线程池线程数量固定为3
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run() {
System.out.println("执行任务啦");
}
};
// 3. 向线程池提交任务
fixedThreadPool.execute(task);
特点:只有核心线程,线程数量固定,执行完立即回收,任务队列为链表结构的有界队列。
应用场景:控制线程最大并发数。
2.定时线程池(ScheduledThreadPool )
// 1. 创建 定时线程池对象 & 设置线程池线程数量固定为5
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run() {
System.out.println("执行任务啦");
}
};
// 3. 向线程池提交任务
scheduledThreadPool.schedule(task, 1, TimeUnit.SECONDS); // 延迟1s后执行任务
scheduledThreadPool.scheduleAtFixedRate(task,10,1000,TimeUnit.MILLISECONDS);// 延迟10ms后、每隔1000ms执行任务```
特点:核心线程数量固定,非核心线程数量无限,执行完闲置 10ms 后回收,任务队列为延时阻塞队列。
应用场景:执行定时或周期性的任务。
3.可缓存线程池(CachedThreadPool)
// 1. 创建可缓存线程池对象
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run() {
System.out.println("执行任务啦");
}
};
// 3. 向线程池提交任务
cachedThreadPool.execute(task);
特点:无核心线程,非核心线程数量无限,执行完闲置 60s 后回收,任务队列为不存储元素的阻塞队列。
应用场景:执行大量、耗时少的任务。
4. 单线程化线程(SingleThreadExecutor)
// 1. 创建单线程化线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run() {
System.out.println("执行任务啦");
}
};
// 3. 向线程池提交任务
singleThreadExecutor.execute(task);
特点:只有 1 个核心线程,无非核心线程,执行完立即回收,任务队列为链表结构的有界队列。
应用场景:不适合并发但可能引起 IO 阻塞性及影响 UI 线程响应的操作,如数据库操作、文件操作等。
总结:斩神行动
1.对于创建线程池依据规范,也要注意核心参数的含义和取值,根据实际场景进行参数配置,
2.对于初学者来说完全够用了,源码会在后面进行发布讲解,我们都是路人,无非走个你前我后。
本文详细介绍了面试中常见的线程池问题,包括线程池的概念、优点、结构、创建规范和常见类型的使用。通过分析ExecutorService接口、ThreadPoolExecutor类及其重要参数,阐述线程池如何降低性能开销、提高响应速度。同时,讨论了四种线程池(FixedThreadPool、ScheduledThreadPool、CachedThreadPool、SingleThreadExecutor)的应用场景和工作流程。

1362

被折叠的 条评论
为什么被折叠?



