写点什么

[小笔记] Java 线程池

作者:java易二三
  • 2023-08-25
    湖南
  • 本文字数:1585 字

    阅读完需:约 5 分钟

除了 newScheduledThreadPool 创建的线程池,其他的默认线程池都是以 ThreadPoolExecutor 对象实现的。

1 默认线程池的实现

1.1 FixedThreadPool

Java复制代码public static ExecutorService newFixedThreadPool(int nThreads) {    return new ThreadPoolExecutor(nThreads, nThreads,                                  0L, TimeUnit.MILLISECONDS,                                  new LinkedBlockingQueue<Runnable>());}
复制代码

1.2 SingleThreadPool

Java复制代码public static ExecutorService newSingleThreadExecutor() {    return new FinalizableDelegatedExecutorService        (new ThreadPoolExecutor(1, 1,                                0L, TimeUnit.MILLISECONDS,                                new LinkedBlockingQueue<Runnable>()));}
复制代码

1.3 CachedThreadPool

Java复制代码public static ExecutorService newCachedThreadPool() {    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                                  60L, TimeUnit.SECONDS,                                  new SynchronousQueue<Runnable>());}
复制代码

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 的线程池设置为以下这样:

Kotlin复制代码val executor = ThreadPoolExecutor(    0, Int.MAX_VALUE,    60, TimeUnit.SECONDS,    LinkedBlockingQueue())
复制代码

导致网络请求只在一个线程中执行,大部分的网络请求任务都在 WorkQueue 中等待,而且还有 OOM 的风险,使用者使用起来就感觉网络特别的慢。

用户头像

java易二三

关注

还未添加个人签名 2021-11-23 加入

还未添加个人简介

评论

发布
暂无评论
[小笔记] Java 线程池_Java_java易二三_InfoQ写作社区