阿里一面:那我把线程池coreSize配置成0会怎样?

写在前面

设想一下,在我们的项目中存在一个边缘的业务流程,它并不频繁地被触发。在设计线程池时,我回想起了线程池的常见配置原则。为了最大限度地节省资源,我将核心线程数(corePoolSize)设置为0。这样的配置是否能够顺利执行任务呢?

线程池配置原则回顾

在任务提交时,线程池会根据以下策略进行处理:

  • 如果线程池中的线程数少于核心线程数,则创建一个新线程来执行任务。
  • 如果线程池中的线程数大于等于核心线程数:
  • 如果阻塞队列未满,则将任务放入阻塞队列中。
  • 如果阻塞队列已满:
    • 如果线程池中的线程数小于最大线程数,则创建一个新线程来执行当前任务。
    • 如果线程池中的线程数大于等于最大线程数,则执行拒绝策略。

基于上述流程,当corePoolSize设置为0时,任务是否会直接进入阻塞队列等待执行?如果队列足够大,任务是否可能因为队列未满而永远无法执行(因为没有空闲线程被创建)?

实践出真知

猜测无益,实践才是检验真理的唯一标准。下面我配置了一个线程池,希望在提交任务时,能够通过“空闲线程”快速处理任务,然后自动销毁,不占用系统资源。那么,这种配置的线程池是否可能因为阻塞队列未满而导致任务迟迟不被执行?

private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(  
    0,  
    8,  
    5, TimeUnit.MINUTES,  
    new LinkedBlockingQueue<>(512),  
    new CallerRunsPolicy()  
);

我们使用这个线程池来打印一个字符串,以验证任务是否能够被执行。

public static void main(String[] args) {  
    MonitorThreadPoolConfig.addThreadPoolExecutor(threadPoolExecutor);  

    threadPoolExecutor.execute(() -> System.out.println("hahahhaha "));  
}

执行结果显示:任务确实被执行了(这里我添加了对线程池的定时监控)。

执行结果

深入源码

public void execute(Runnable command) {  
    if (command == null)  
        throw new NullPointerException();  
    /*  
    * 执行分为三个步骤:  
    *  
    * 1. 如果运行的线程数少于核心线程数,尝试  
    * 用给定的命令作为其第一个任务启动一个新线程。  
    * addWorker原子检查runState和workerCount,  
    * 因此可以防止在不应该添加线程时添加线程。  
    *  
    * 2. 如果任务可以成功入队,那么我们仍然需要  
    * 再次检查是否应该添加一个线程(因为现有的线程可能已经死亡)  
    * 或者池是否已经关闭。因此,如果停止了,我们需要回滚入队操作,  
    * 或者如果没有线程,就启动一个新线程。  
    *  
    * 3. 如果我们不能将任务入队,那么我们尝试添加一个新线程。  
    * 如果失败,我们知道我们已经关闭或者饱和了,  
    * 因此拒绝任务。  
    */  
    int c = ctl.get();  
    if (workerCountOf(c) < corePoolSize) {  
        if (addWorker(command, true))  
            return;  
        c = ctl.get();  
        if (isRunning(c) && workQueue.offer(command)) {  
            int recheck = ctl.get();  
            if (! isRunning(recheck) && remove(command))  
                reject(command);  
            else if (workerCountOf(recheck) == 0)  
                addWorker(null, false);  
        }  
        else if (!addWorker(command, false))  
            reject(command);  
    }
}

从源码的第34行到第35行可以看出:当核心线程数为0时,会创建一个新的线程来执行当前任务。

进一步探讨

既然可以通过设置corePoolSize为0来最小化“常驻”线程的资源占用,那么是否有其他方法可以达到相同的效果呢?

有经验的开发者可能会立刻回答:从Java 1.6开始,ThreadPoolExecutor有一个属性可以实现核心线程超时销毁的功能。

/**  
* 如果设置为false(默认),即使空闲,核心线程也会保持活动状态。  
* 如果设置为true,核心线程将使用keepAliveTime来超时等待工作。  
*/  
private volatile boolean allowCoreThreadTimeOut;

我们可以将线程池配置如下

文章整理自互联网,只做测试使用。发布者:Lomu,转转请注明出处:https://www.it1024doc.com/4405.html

(0)
LomuLomu
上一篇 2024 年 12 月 26 日 上午4:21
下一篇 2024 年 12 月 26 日 上午5:22

相关推荐

  • Java刷题常见的集合类,各种函数的使用以及常见的类型转化等等

    目录 前言 集合类 ArrayList 1. 创建和初始化 ArrayList 2.添加元素 add 3.获取元素 get 4.删除元素 remove 5.检查元素 6.遍历 ArrayList LinkedList Stack 1. 创建Stack对象 2. 压入元素 (push) 3. 弹出元素 (pop) 4. 查看栈顶元素 (peek) 5. 检查栈…

    2025 年 1 月 5 日
    66900
  • 网站动静加速架构 dcdn+ga 全站加速和全球加速api

    # 背景 我们的公司提供的所有服务均位于香港,这意味着我们的客户,主要分布在中国内地,访问这些服务时可能会遇到速度较慢的问题。由于我们专注于NFT领域,因此选择在香港提供服务。 # 一、加速策略 ## 1.1 静态资源加速 静态资源加速是指对如HTML、JavaScript、CSS和图像文件等静态文件的快速分发。利用云服务提供商的CDN服务,我们可以有效地提…

    未分类 2024 年 12 月 24 日
    62500
  • 【Java 学习】详细讲解—包和导包、Scanner类、输入源

    1. 包 1.1 包的概念 想象一下,你和你的同学们来自不同的家庭,每个家庭都有自己的生活方式和空间。如果这些家庭都住在同一个屋檐下,那么个人的习惯和空间就会相互干扰。同理,在软件开发中,不同的程序也需要有各自的“空间”以保持独立性,这就是包(Package)的作用。包可以被看作是一组文件夹,它们允许在不同的文件夹中存在同名的文件,从而实现隔离。 1.2 包…

    未分类 2024 年 12 月 27 日
    56600
  • 2024年主流使用的JDK版本及其核心特性介绍

    原文全文详见个人博客:https://www.coderli.com/feature-of-popular-jdk-version/2024年主流使用的JDK版本及其核心特性介绍在群里交流的时候,经常遇到不同同学使用的JDK版本五花八门,并且很多不知道其使用版本的特点。作为Java开发者,理解不同JDK版本的核心特性和优势不仅能提高我们的开发效率,还能帮助我…

    2024 年 12 月 28 日
    86400
  • 2025年Java开发环境搭建全指南(附详细图文步骤)

    目录导航 2025年Java开发环境搭建全指南 1. Java开发工具包概述 2. 获取JDK安装包 3. JDK安装流程详解 4. 系统环境变量设置 5. 安装结果验证 6. 编写并运行首个Java应用 6.1 编写示例代码 6.2 程序编译与执行 6.3 文件扩展名管理技巧 2025年Java开发环境搭建全指南 1. Java开发工具包概述 Java开发…

    2025 年 5 月 14 日
    50200

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信