[小笔记] Java 线程池
除了 newScheduledThreadPool
创建的线程池,其他的默认线程池都是以 ThreadPoolExecutor
对象实现的。
1 默认线程池的实现
1.1 FixedThreadPool
1.2 SingleThreadPool
1.3 CachedThreadPool
2 ThreadPoolExecutor
2.1 CorePoolSize
核心线程数。当目前的线程数小于设置的核心线程数时,有新的任务来时会直接创建一个新的线程来执行任务,就算当前有 Idel 的线程也会创建;当当前的线程数大于等于核心线程数时,就会把任务加入等待队列(后边说)。
核心线程可以在创建线程池时直接创建,而不用等待运行时创建
核心线程默认是不会回收的,可以通过设置参数让其 Idel 一段时间后让其回收
2.2 MaximumPoolSize
最大线程数。MaximumPoolSize - CorePoolSize 就是普通线程数。当核心线程数和等待队列都满的时候,但是最大线程数没有满,就会创建非核心线程执行;最大线程数满的时候就会触发拒绝策略。(后边说)根据 KeepAliveTime 参数,禁止一段时间后会回收(也可以不回收)。
2.3 KeepAliveTime
非核心线程回收间隔时间。当非核心线程 Idel 后,超过该设置时间就会被回收,如果想不被回收需要把超时时间设置为 Long.MAX
。
2.4 WorkQueue
任务队列。当核心线程数满的时候存放等待的任务,当线程空闲时按顺序递交任务给线程执行。下面列举一些有代表性的 Queue。
2.4.1 LinkedBlockingQueue
无限制等待任务队列的大小,也就是说核心线程数设置不为 0 的情况下,非核心线程是不会创建的。
2.4.2 SynchronousQueue
等待线程的大小为 0,直接递交任务给 Thread 执行。
2.4.3 ArrayBlockingQueue
可以设置等待任务队列的大小
2.5 拒绝策略
2.5.1 AbortPolicy
默认策略,直接抛出异常。
2.5.2 DiscardPolicy
直接丢弃当前任务。
2.5.3 DiscardOldestPolicy
丢弃一个队列中最旧的任务。
2.5.4 CallerRunsPolicy
直接在提交任务的线程执行这个任务。
2.6 总结
提交一个任务的不同情况分析:
2.6.1 核心线程数有空闲
如果核心线程数没有满,创建新的线程执行;如果核心线程数满了,但是有 Idel 的核心线程,直接用 Idel 的核心线程执行。
2.6.2 核心线程已满,但任务队列不满
将任务加入到队列中,等有线程空闲了,从队列中取任务执行。
2.6.3 核心线程和任务队列都已满,但未达到最大线程数
创建非核心线程执行任务。
2.6.4 所有都达到上限
执行拒绝策略。
3 最后
LinkedBlockingQueue 是有 OOM 风险的,当任务过于多时都会堆积在 Queue 中,导致 OOM。这也是为什么有的公司禁止使用 SingleThreadPool 和 FixedThreadPool,不过一般客户端都还好,主要是服务端。我在开发中还发现有大聪明把 OkHttp 的线程池设置为以下这样:
导致网络请求只在一个线程中执行,大部分的网络请求任务都在 WorkQueue 中等待,而且还有 OOM 的风险,使用者使用起来就感觉网络特别的慢。
评论