1.为什么要有线程池?
在一个应用程序中,我们需要多次使用线程,也就意味着,我们需要多次创建并销毁线程。而创建并销毁线程的过程势必会消耗内存。而在Java中,内存资源是及其宝贵的,所以,我们就提出了线程池的概念。
首先线程是很宝贵的资源,不断的创建线程会造成资源的大量浪费引发oom
线程池能够降低资源的消耗,任务可以不需要等到线程创建就能立即执行(速度快),同时提高的对资源的可管理性
2.如何创建线程池?
创建时我们用ThreadPoolExecutor类
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数含义
corePoolSize(必需):核心线程数。默认情况下,核心线程会一直存活,但是当将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
maximumPoolSize(必需):线程池所能容纳的最大线程数。当活跃线程数达到该数值后,后续的新任务将会阻塞。
keepAliveTime(必需):线程闲置超时时长。如果超过该时长,非核心线程就会被回收。如果将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
unit(必需):指定 keepAliveTime 参数的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。
workQueue(必需):任务队列。通过线程池的 execute() 方法提交的 Runnable 对象将存储在该参数中。其采用阻塞队列实现。
threadFactory(可选):线程工厂。用于指定为线程池创建新线程的方式。
handler(可选):拒绝策略。当达到最大线程数时需要执行的饱和策略。
线程池的处理流程线程池本质上是一个HashSet
handler的拒绝策略:
有四种:第一种AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满
第二种DisCardPolicy:不执行新任务,也不抛出异常
第三种DisCardOldSetPolicy:将任务队列中的第一个任务替换为当前新进来的任务执行
第四种CallerRunsPolicy:直接调用execute来执行当前任务
线程池的实现原理重点就在于它维护了一个ctl参数,这个ctl参数的用高3位表示线程池的状态,低29位来表示线程的数量
同时hreadPoolExecutor内部源码运用了大量位运算。