线程池的作用:
- 通过重复利用已创建的线程,降低线程创建和销毁造成的消耗。
- 提⾼响应速度。不需要等线程创建。
- 提⾼线程的可管理性。线程池统⼀分配管理。
创建线程池的方式有两种
使用 Executors创建线程池
用ThreadPoolExecutor创建
不推荐使用 Executors创建线程池,要用ThreadPoolExecutor创建,因为ThreadPoolExecutor能指定线程池的重要参数。
Executors 返回线程池对象的弊端:
FixedThreadPool 和 SingleThreadExecutor:
允许请求的队列⻓度为Integer. MAX_VALUE可能堆积⼤量的请求,从而导致OOM(OutOfMemory,即内存泄漏或溢出)。
CachedThreadPool 和 ScheduledThreadPool:
允许创建的线程数量为Integer. MAX_VALUE,可能会创建⼤量线程,从而导致OOM。
ThreadPoolExecutor 3个重要参数:
corePoolSize :核心线程池大小,即最少可同时运⾏的线程数量。
maximumPoolSize : 最⼤线程数。
workQueue :任务存储队列
线程池的工作流程
线程池刚启动时,核心线程数为0
向线程池提交新任务时,会开启新线程来执行
若线程数< corePoolSize,即使工作线程处于空闲状态,也会创建一个新线程来执行新任务
若线程数>=corePoolSize,则将任务放到workQueue任务队列中
当workQueue满时,
若线程数<maximumPoolSize,则创建一个新的非核心线程来运行任务
若>=maximumPoolSize,则直接拒绝,抛出异常
其他常⻅参数:keepAliveTime、unit、threadFactory、handler
keepAliveTime:线程的存活时间
若线程池当前的线程数>corePoolSize,则多余的线程(超出corePoolSize的线程)若空闲时间超过keepAliveTime,那么这些多余的线程就会被回收。
unit:存活时间的单位
handler:拒绝策略,任务量超出线程池的限制或shutdown后还在继续提交任务,则执行handler拒绝。
默认采用AbortPolicy,线程池直接拒绝,抛出RejectedExecutionException异常
线程池的几种拒绝策略:
AbortPolicy:抛出RejectedExecutionException异常,拒绝处理新任务。
CallerRunsPolicy :调用execute函数的上层线程(main线程?)去执行被拒绝的任务,可能会阻塞主线,造成延迟,但它不会丢弃任何⼀个任务请求(会增加队列容量)。
同样线程池拒绝执行该任务,但是它会调用上层线程(main线程)去执行,所以该任务在main线程中运行,main线程被阻塞,当这个任务执行完后,添加下一个任务时,前面的任务可能也已经执行完(有了空闲线程),则下一线程又可在线程池中执行。
DiscardPolicy:直接丢弃,不会抛异常也不执行
DiscardOldestPolicy:丢弃任务队列中最早入队的任务,再把这个新任务添加进去。