面试之线程池

112 阅读2分钟

为什么要用线程池

1.降低资源消耗。避免频繁的创建和销毁线程;
2.提升响应速度。不需要创建线程去执行请求;
3.方便管理线程。提高线程复用率。

线程池的基本原理

线程池是通过java的ThreadPoolExecutor类实现的,顶层接口是Executor。
ThreadPoolExecutor类提供了如下的构造方法:
public ThreadPoolExecutor(
       int corePoolSize,
       int maximumPoolSize,
       long keepAliveTime,
       TimeUnit unit,
       BlockingQueue workQueue,
       ThreadFactory threadFactory,
       RejectedExecutionHandler handler
)
让我们来分析一下七个参数的作用:
1. corePoolSize:核心线程数。核心线程只要创建了默认情况下不会销毁,会一直存在于线程池中。
2. maximumPoolSize:最大线程数。线程池允许创建的最大线程数,即核心线程数与非核心线程数之和。
3. keepAliveTime:非核心线程存活时间。过了时间便会销毁。
4. unit:单位。
5. workQueue:任务队列。存放待执行的任务,有多种类型,下面会有详细说明。
6. threadFactory:线程工厂。可以设置线程的参数,不指定的情况下会创建一个默认的。
7. handler:拒绝策略。队列满了以后的处理策略,下面会有详细说明。

任务队列详细说明:
1. LinkedBlockingQueue:基于链表的阻塞队列,默认大小Interger.MAX_VALUE。
2. ArrayBlockingQueue:基于数组的阻塞队列,需要指定大小。
3. SynchronousQueue:同步队列,容量为0,每次put一个新的任务时需要等待take操作,反之亦然。
4. DelayQueue:延迟队列,只有当延迟时间到了,才能获取到队列中的任务。
5. PriorityBlockingQueue:优先级队列,任务具有优先级的队列。

拒绝策略详细说明:
1.ThreadPoolExecutor.AbortPolicy:默认拒绝处理策略,丢弃任务并抛出异常。
2.ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。
3.ThreadPoolExecutor.DiscardOldestPolicy:丢弃最旧的的任务。
4.ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。

四种常见线程池

1.newFixedThreadPool:固定数量的线程池,使用LinkedBlockingQueue作为任务队列,有OOM风险;
2.newCachedThreadPool:可缓存线程的线程池,使用SynchronousQueue作为任务队列,有OOM风险;
3.newSingleThreadPool:单线程的线程池,使用LinkedBlockingQueue作为任务队列,有OOM风险;
4.newScheduledThreadPool:定时和周期性线程池,使用DelayQueue作为任务队列,有OOM风险;
综上所述,因为存在OOM风险,所以不建议使用Executors直接创建线程池。

线程池处理流程

当有一个新任务需要处理时,如果核心线程未满,则创建一个核心线程用来执行该任务,如果核心线程已满,则将任务放入任务队列,如果任务队列已满,则创建非核心线程执行任务,如果创建的线程数已经达到了最大线程数并且任务队列已满,则执行拒绝策略。

image.png

主要为了巩固基础,加深记忆,没有涉及过多深入的知识,欢迎讨论和提问。