参考:
为什么要用线程池
当我们需要的并发线程数量很多时,且每个线程执行很短的时间就结束了,这样,我们频繁的创建线程、销毁线程就大大降低了工作效率(创建与销毁线程需要时间和资源)。
java的线程池可以达到这样的效果,一个线程执行完任务后,继续执行下一个任务,不被销毁,这样线程的利用率就提高了。
6个参数
1、corePoolSize:核心线程数
核心线程会一直存活,即使没有任务需要执行
当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭。
2、queueCapacity:任务队列容量(阻塞队列)
当核心线程数达到最大时,新任务会放在队列中排队等待执行
3、maxPoolSize:最大线程数
当线程数<=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常
值得注意的是如果使用了无界的任务队列这个参数就没什么效果。例如new LinkedBlockingQueue<Runnable>()
BlockingQueue
workQueue 这个缓冲队列有三种类型:
直接提交SynchronousQueue
无界队列LinkedBlockingQueue
有界队列ArrayBlockingQueue
4、 keepAliveTime:线程空闲时间
当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
如果allowCoreThreadTimeout=true,则会直到线程数量=0
一般在线程数量大于核心线程数量时才会起作用,如果一个线程空闲时间达到keepAliveTime时会被终止,直到线程数不超过keepAliveTime
5、 allowCoreThreadTimeout:允许核心线程进行超时退出。当核心线程超时后也会退出
6、 rejectedExecutionHandler:任务拒绝处理句柄。
- 两种情况会拒绝处理任务:
- 当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务
- 用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务
- 线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常
- ThreadPoolExecutor类有几个内部实现类来处理这类情况:
- AbortPolicy 丢弃任务,抛运行时异常
- RunsPolicy 执行任务
- DiscardPolicy 忽视,什么都不会发生
dOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务 - 实现RejectedExecutionHandler接口,可自定义处理器
ThreadPoolExecutor执行顺序
线程池按以下行为执行任务
- 当线程数小于核心线程数时,创建线程。
- 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
- 当线程数大于等于核心线程数,且任务队列已满
- 若线程数小于最大线程数,创建线程
- 若线程数等于最大线程数,抛出异常,拒绝任务
线程池状态
状态 | 解释 |
---|---|
RUNNING | 运行态,可处理新任务并执行队列中的任务 |
SHUTDOW | 关闭态,不接受新任务,但处理队列中的任务 |
STOP | 停止态,不接受新任务,不处理队列中任务,且打断运行中任务 |
TIDYING | 整理态,所有任务已经结束,workerCount = 0 ,将执行terminated()方法 |
TERMINATED | 结束态,terminated() 方法已完成 |
与线程六个状态不同概念: 线程状态