介绍
线程池就是管理线程的池子,当有任务要处理时,不用频繁创建新线程,而是从池子拿个线程来处理。
当任务执行完,线程并不会销毁,而是在等待下一个任务。
因此可以节省资源,提高响应速度。 还可以提高线程的可管理性。
execute()提交任务执行逻辑
当有任务提交的时候,首先判断核心线程池是否已满,如果未满,创建核心线程执行任务,如果满了将任务添加到队列中,如果队列也满了,判断线程池是否已满,如果未满,创建非核心线程执行任务,如果已满,执行饱和策略。
线程池的参数
7个参数:
饱和策略
线程池的饱和策略主要有四种类型:
-
AbortPolicy 抛出一个异常,默认的
-
DiscardPolicy 新提交的任务直接被抛弃 空实现,直接吞掉
-
DiscardOldestPolicy 丢弃队伍里面最老的任务(去掉队头元素),将当前这个任务继续提交给线程池
-
CallerRunsPolicy 交给线程池调用所在的线程进行处理,即将某些任务回退到调用者
还可以实现RejectedExecutionHandler接口来实现自定义的handler
threadFactory的用处
- 给线程设置自定义的名字
- 可以设置为守护线程,当程序结束时保证JVM安全退出。
- 可以设置异常处理器
Executors实现的线程池
-
Executors.newSingleThreadExecutor()
用到LinkedBlockingQueue,core为1,max为1
适用场景:串行执行任务,一个任务一个任务地执行。 -
Executors.newFixedThreadPool()
用到LinkedBlockingQueue,core为设定值,max为设定值
适用场景:处理CPU密集型任务,尽可能少的分配线程,适用执行长期的任务。 -
Executors.newCachedThreadPool()
用到SynchronousQueue,core为0,max为MAX_VALUE
适用场景:并发执行大量短期的小任务。 -
Executors.newScheduledThreadPool()
用到DelayedWorkQueue,core为设定值,max为MAX_VALUE
适用场景:周期性执行任务。 -
Executors.newWorkStealingPool()
内部会构建ForkJoinPool,利用work-stealing算法,并行处理任务,不保证处理顺序。
线程池使用不当的问题
- 内存飙升
线程池未关闭而造成线程无法被回收,线程数持续增加。 - 线程死锁
如果提交到相同线程池的任务不是相互独立的,而是有依赖关系的,那么就有可能导致线程死锁。
具体现象是应用每运行一段时间偶尔就会处于无响应的状态,监控数据看上去一切都正常,但是实际上已经不能正常工作了。
解决:为不同的任务创建不同的线程池
如何设置线程池最大线程数
获取核数:Runtime.getRunTime().avaliableProcessors()
CPU密集型: 核数 + 1
为什么要加一?
计算(CPU)密集型的线程恰好在某时因为发生一个页错误或者因其他原因而暂停,刚好有一个“额外”的线程,可以确保在这种情况下CPU周期不会中断工作。
IO密集型 : 线程数 = CPU核数 * (1 + 平均等待时间(I/O耗时) / 平均工作时间(CPU耗时))