线程池是什么
池化技术能够减少资源对象的创建次数,提高程序的响应性能,特别是在高并发下这种提高更加明显
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务
为什么要用线程池
主要是为了减少每次获取资源的消耗,提高对资源的利用率。线程池提供了一种限制和管理资源(包括执行一个任务)。每个线程池还维护一些基本统计信息,例如已完成任务的数量。
降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系
统的稳定性,使用线程池可以进行统一的分配,调优和监控。
JDK源码中线程池参数的定义
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* /
corePoolSize:核心线程池数量
线程池中一直保持的线程的数量,即使线程空闲。除非设置了 allowCoreThreadTimeOut
maximumPoolSize:最大线程池数量
指定线程池中允许的最大的线程数
keepAliveTime:空闲线程存活时间
当线程数大于核心线程数的时候,超出核心线程数的线程在最大多长时间没有接到新任务就会终止释放 ,最终线程池维持在 corePoolSize 大小;
即超过corePoolSize的空闲线程,在多长时间内会被销毁
unit:keepAliveTime的时间单位
workQueue:阻塞队列
阻塞队列,用来存储等待执行的任务,如果当前对线程的需求超过了 corePoolSize大小, 就会放在这里 等待空闲线程执行.
threadFactory:创建线程工厂
创建线程的工厂,比如指定线程名等,使用默认即可
handler:拒绝策略
拒绝策略,如果当前任务太多来不及处理,线程池就会使用拒绝策略。
JDK默认有以下拒绝策略
- AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作。
- CallerRunsPolicy 策略:只要线程池未关闭,该策略直接在调用者线程中,运行当前被
丟弃的任务。显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急
剧下降。 - DiscardOledestPolicy 策略:该策略将丢弃最老的一个请求,也就是即将被执行的一个任
务,并尝试再次提交当前任务。 - DiscardPolicy策略:该策略默默地丟弃无法处理的任务,不予任何处理。如果允许任务
丢失,我觉得这可能是最好的一种方案了吧!
线程池使用Demo
使用ThreadPoolExecutor创建一个线程池
RunnableTest实现Runnable接口
运行结果:
运行流程
线程池运行流程
- 初始化线程池,准备好 corePoolSize数量的核心线程,准备接受任务
- 新的任务进来,用 corePoolSize准备好的空闲线程执行。
(1) 、corePoolSize满了,就将再进来的任务放入阻塞队列中。空闲的 corePoolSize就会自己去阻塞队列获取任务执行
(2) 、阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量
(3) 、maximumPoolSize 都执行好了。maximumPoolSize 数量空闲的线程会在 keepAliveTime 指定的时间后自动销毁。最终保持到corePoolSize大小
(4) 、如果线程数开到了 maximumPoolSize 的数量,还有新任务进来,就会使用 handler指定的拒绝策略进行处理 - 所有的线程创建都是由指定的 threadFactory创建的。
经典面试题:
- 题目:一个线程池 corePoolSize:10;maximumPoolSize:30;workQueue:50 ;100的 并发进来如何分配线程的
- 答案:先有 10 个线程进入核心线程池直接得到执行,接下来 50 个进入阻塞队列排队,在多开 20个线程达到最大线程池数量继续执行。现在已经有 80 个线程被安排上了。剩下 20 个线程使用拒绝策略。