2023-03-22  阅读(3)
原文作者:简单教程 原文地址:https://www.twle.cn/c/yufei/javatm/javatm-basic-executorservice.html

因为上一章节篇幅有限,所以我决定把 一文秒懂 Java 线程池 拆分为三篇文章单独介绍。本章节,我们就来看看 ThreadPoolExecutor

ThreadPoolExecutor

ThreadPoolExecutor 是一个可被继承 ( extends ) 的线程池实现,包含了用于微调的许多参数和钩子。

我们并不会讨论 ThreadPoolExecutor 类中的所有的参数和钩子,只会讨论几个主要的配置参数:

  1. corePoolSize
  2. maximumPoolSize
  3. keepAliveTime

ThreadPoolExecutor 创建的线程池由固定数量的核心线程组成,这些线程在 ThreadPoolExecutor 生命周期内始终存在,除此之外还有一些额外的线程可能会被创建,并会在不需要时主动销毁。corePoolSize 参数用于指定在线程池中实例化并保留的核心线程数。如果所有核心线程都忙,并且提交了更多任务,则允许线程池增长到 maximumPoolSize

keepAliveTime 参数是额外的线程( 即,实例化超过 corePoolSize 的线程 )在空闲状态下的存活时间。

这三个参数涵盖了广泛的使用场景,但最典型的配置是在 Executors 静态方法中预定义的。

Executors.newFixedThreadPool()

例如,Executors.newFixedThreadPool() 静态方法创建了一个 ThreadPoolExecutor ,它的参数 corePoolSizemaximumPoolSize 都是相等的,且参数 keepAliveTime 始终为 0 ,也就意味着此线程池中的线程数始终相同。

    ThreadPoolExecutor executor = 
      (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
    executor.submit(() -> {
        Thread.sleep(1000);
        return null;
    });
    executor.submit(() -> {
        Thread.sleep(1000);
        return null;
    });
    executor.submit(() -> {
        Thread.sleep(1000);
        return null;
    });
    
    assertEquals(2, executor.getPoolSize());
    assertEquals(1, executor.getQueue().size());

上面这个示例中,我们实例化了一个固定线程数为 2ThreadPoolExecutor。这意味着如果同时运行的任务的数量始终小于或等于 2 ,那么这些任务会立即执行。否则,其中一些任务可能会被放入队列中等待轮到它们。

上面这个示例中,我们创建了三个 Callable 任务,通过睡眠模拟 1000 毫秒的繁重工作。前两个任务将立即执行,第三个任务必须在队列中等待。我们可以通过在提交任务后立即调用 getPoolSize()getQueue().size() 来方法来验证。

Executors.newCachedThreadPool()

Executors 还提供了 Executors.newCachedThreadPool() 静态方法创建另一个预配置的 ThreadPoolExecutor。该方法创建的线程池没有任何核心线程,因为它将 corePoolSize 属性设置为 0,但同时有可以创建最大数量的额外线程,因为它将 maximumPoolSize 设置为 Integer.MAX_VALUE ,且将 keepAliveTime 的值设置为 60 秒。

这些参数值意味着缓存的线程池可以无限制地增长以容纳任何数量的已提交任务。但是,当不再需要线程时,它们将在 60秒不活动后被销毁。这种线程池的使用场景一般是你的应用程序中有很多短期任务。

    ThreadPoolExecutor executor = 
      (ThreadPoolExecutor) Executors.newCachedThreadPool();
    executor.submit(() -> {
        Thread.sleep(1000);
        return null;
    });
    executor.submit(() -> {
        Thread.sleep(1000);
        return null;
    });
    executor.submit(() -> {
        Thread.sleep(1000);
        return null;
    });
    
    assertEquals(3, executor.getPoolSize());
    assertEquals(0, executor.getQueue().size());

上面这个示例中的队列大小始终为 0 ,因为在内部使用了 SynchronousQueue 的实例。在 SynchronousQueue 中,插入和删除操作总是成对出现且同时发生。因此队列实际上从不包含任何内容。

Executors.newSingleThreadExecutor()

Executors.newSingleThreadExecutor() 静态方法则创建另一种典型的只包含单个线程的 ThreadPoolExecutor 实例。

这种单线程执行程序是创建事件循环的理想选择。

在这个单线程 ThreadPoolExecutor 实例中,属性 corePoolSize 和属性 maximumPoolSize 的值都为 1,而属性 keepAliveTime 的值为 0

在单线程 ThreadPoolExecutor 实例中,所有的任务都按顺序执行。因此,下面的示例中,任务完成后标志的值是 2

    AtomicInteger counter = new AtomicInteger();
    
    ExecutorService executor = Executors.newSingleThreadExecutor();
    executor.submit(() -> {
        counter.set(1);
    });
    executor.submit(() -> {
        counter.compareAndSet(1, 2);
    });

此外,单线程 ThreadPoolExecutor 实例使用了不可变包装器进行修饰,因此在创建后无法重新配置。当然了,这也是我们无法将该示例强制转换为 ThreadPoolExecutor 的原因。


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文