线程池线程保活以及动态更新线程数

news2024/11/18 19:42:35

基本知识

各位大佬在面试过程肯定会被问到线程池或者多线程的问题,例如:

  • 线程池核心参数及其作用
  • 线程池添加任务的执行顺序
  • 任务队列以及任务的拒绝策略
  • 等等

这些问题是相信稍微研究过线程池JDK源码的都能掌握。有兴趣的可以参数这篇博文。
在进入今天正题之前,还是来看看第二个问题:线程池添加任务后,线程池内部是如何进行执行任务的。假设线程池中

  • 核心线程数为1
  • 最大线程数为2
  • 队列是长度为10的有界阻塞队列

线程池执行任务顺序如下:

  1. 当添加第一个任务(任务A)时,判断当前线程数是否小于核心线程数,是则直接创建新的线程执行该任务。
  2. 假设任务A还没有执行完,继续添任务B,此时当前线程数等于核心线程数,线程池会直接将任务添加到阻塞队列中。
  3. 在任务A还没有执行完的情况下,持续不断地添加任务,直到队列填满了任务,还有任务C需要添加,此时会判断线程数是否大于等于最大线程数,小于则会创建新的线程执行任务C,大于则拒绝策略处理
  4. 当线程池2个线程都在工作且队列已满,任务D需要执行时,线程池则会根据任务拒绝策略处理任务D

在了解了线程池执行任务的原理后,再来看看如下几个问题

  • 线程保活。
  • 线程池如何动态更新核心线程数和最大线程数?
  • 提交Callable任务时,线程之间是如何传递值的?
  • 多余的线程是如何结束生命周期的?

线程保活

假设线程(简称T)执行任务A结束后,间隔5s后再向线程池添加任务B,思考这5s线程T是如何保活的或者说它是什么状态。添加任务B的线程是如何让线程T执行任务B的。如果不理解线程的状态转换和阻塞队列的原理,建议劝退。

  1. 线程A执行execute(taskA)时,会调用addWorker()方法创建线程T,并调用T.start()启动线程T。线程T会执行runWorker()开始while循环,循环条件是第一个任务不等于null或者getTask()(从任务队列里获取任务)不等于null,显然此时第一个任务为taskA,线程T会执行taskA.run()执行任务。执行完taskA后,继续循环判断是否存在第一个任务或者从任务队列里获取任务,显然第一个任务已经执行完成,线程T再执行getTask()会调用队列的take()获取任务,此时队列为空,线程T则会执行Condition.await()方法(即AQS的await方法),await()方法中会调用LockSupport.park(this),最终将自己设置为WAITING状态,也就是所谓的保活(或者说挂起)。
  2. 5s之后线程B执行execute(taskB),此时线程数量等于核心线程数,线程B会直接调用队列的offer方法把taskB放入队列中,offer方法加锁后调用enqueue方法,将taskB加入队列后执行Condition.signal(),signal方法最终会调用LockSupport.unpark(线程T),即唤醒处于WAITING状态的线程T,将它的状态设置为RUNNABLE。

小结:线程池利用阻塞队列实现任务的排队等待,而阻塞队列包含ReentrantLock属性,利用AQS的条件等待队列调用LockSupport.park(this)将自己设置为WAITING状态。

提交Callable任务

线程池提供了submit()方法来提交Callable任务,实际底层是将Callable包装成FutureTask(它实现了Runnable),然后调用execute()来提交任务
在这里插入图片描述

  1. 当线程A执行execute()方法后返回,紧接着调用FutureTask.get()等待结果返回,在get()判断线程池中任务是否执行完成,是则返回结果,否则调用awaitDone(),通过LockSupport.park(this)将自己设置为WAITING状态。
  2. 线程池中的线程开始调用FutureTask.run()方法,run()会调用Callable.call(),它的返回结果result会被赋值给FutureTask的outcome属性,然后将任务执行状态设置为NORMAL(2)状态,并调用LockSupport.unpark(t)唤醒线程A
  3. 线程A唤醒之后判断任务执行状态大于COMPLETING(1),立即返回,最后返回outcome。

小结: submit()执行回调任务也是利用LockSupport来进行线程之间的值传递。

动态更新线程数

直接上代码:

public class ThreadPoolTest {

    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10));

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        executor.execute(new RunnableTest(executor));
        TimeUnit.SECONDS.sleep(5);
        executor.setCorePoolSize(2);
        executor.setMaximumPoolSize(3);
    }

    static class RunnableTest implements Runnable {

        private ThreadPoolExecutor executor;

        RunnableTest(ThreadPoolExecutor executor) {
            this.executor = executor;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程数:" + executor.getPoolSize());
                System.out.println("核心线程数:" + executor.getCorePoolSize());
                System.out.println("最大线程数:" + executor.getMaximumPoolSize());
                System.out.println("活跃数量:" + executor.getActiveCount());
            }
        }
    }
}

线程池提供了两个set方法API设置核心线程数和最大线程数

多余线程是如何结束生命周期的

线程池所谓核心线程数即线程池需要处理的任务个数小于等于核心线程数时,多余的线程会结束自己的生命周期。
回到线程保活的思路,线程在getTask()中阻塞在队列的take()方法中,而在这之前会判断当前线程池中线程数量是否大于核心线程数,小于则会阻塞在take()中,大于则调用poll(aliveTime)进行阻塞,底层会调用LockSupport.parkNanos(this, aliveTime)。在aliveTime时间范围内,如果没有多余的任务需要执行,则该线程不会再被唤醒。过了aliveTime时间后,该线程自动唤醒跳出循环执行processWorkerExit()。最终调用Thread.interrupt()方法退出。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1105946.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

去除照片中多余人物方法分享-这些方法快收藏起来

拍照时经常会碰到一些意外的情况&#xff0c;比如不小心捕捉到了一些不需要的人或物&#xff0c;这会影响照片的美观效果&#xff0c;因此学习如何去除照片中多余人物就显得特别重&#xff0c;下面分享一些常用的去除照片中多余人物的方法&#xff0c;如果你也感兴趣的话&#…

使用按钮从 SAP 系统内打开 Excel 文件

了解如何通过 SAP 屏幕上创建的按钮打开所需的 Excel 文件。为了演示这一点&#xff0c;将指导您完成以下步骤。 使用 del 命令删除 SAP 上不必要的元素添加一个按钮&#xff0c;单击后打开弹出窗口创建一个函数来选择 excel 文件创建打开所需 excel 文件的函数 定制 登录 S…

Linux下程序(C语言)实现对文件的复制

目标&#xff1a; 使用系统调用实现cp命令。 原理&#xff1a; 使用系统调用fopen打开文件&#xff0c;使用fgets()从文件读数据&#xff0c;使用fputs() 向文件写数据。 linux 文件 创建命令为 vi (文件名&#xff09;.c 文件源码&#xff1a; #include<stdio.h>…

linux安装达梦数据库(命令行安装)

安装达梦数据库 创建安装用户 1,创建安装用户组dinstall [rootdmDMServer1 ~]# groupadd -g 12345 dinstallgroupadd : 创建组 -g : 指定组id&#xff08;GID&#xff09; 12345&#xff1a; 指定的组名称 dinstall &#xff1a; 组名 2,创建安装用户dmdba [rootdmDMSe…

如何使用 Python 在终端中创建令人惊叹的图形

说明 在当今数据驱动的世界中&#xff0c;可视化或图形确实表达了一个胜过千言万语的故事。可视化提供了快速有效的通信媒体来传达数据&#xff0c;如果与文本或表格共享&#xff0c;则可能难以理解。 虽然有许多强大的可视化工具可用&#xff0c;但有时我们需要一种快速简便的…

SpringBoot的多环境切换(已废除)

profile是Spring对不同环境提供不同配置功能的支持&#xff0c;可以通过激活不同的环境版本&#xff0c;实现快速切换环境 application-dev.yaml server:port: 8082 application-test.yaml server:port: 8081 我们在主配置文件编写的时候&#xff0c;文件名可以是 applicat…

58 乘积最大子数组

乘积最大子数组 题解1 DP换成三个变量&#xff08;因为是连续的&#xff0c;只和上一个状态有关的&#xff0c;所以三个暂存变量迭代就行&#xff09; 给你一个整数数组 nums &#xff0c;请你找出数组中乘积最大的 非空连续子数组&#xff08;该子数组中至少包含一个数字&am…

出席第三届“一带一路”峰会的斯里兰卡总统会见深兰科技高层

10月16日&#xff0c;应斯里兰卡投资促进部的邀请&#xff0c;深兰科技集团董事副总裁刘园桂、集团营销中心总经理徐诗彪一行出席了在北京举行的“中斯投资论坛”。 图&#xff1a;刘园桂女士向斯里兰卡总统赠送熊猫智能公交模型 论坛期间&#xff0c;来北京参加第三届“一带一…

菜鸟智谷产业园正式开园!入驻企业可享受“城区+园区”双重政策扶持

位于杭州未来科技城的菜鸟智谷产业园正式开园了! 杭州未来科技城发布新电商产业加速“百千万亿”计划,菜鸟智谷产业园发布“产业赋能计划”,并设立天猫品牌孵化基地,落户中国(杭州)跨境电商综试区一带一路电商运营中心,引入悦汇跨境产业创新基金……10月17日,杭州未来科技城(海…

【ELK 使用指南 1】ELK + Filebeat 分布式日志管理平台部署

ELK和EFLK 一、前言1.1 日志分析的作用1.2 需要收集的日志1.3 完整日志系统的基本特征 二、ELK概述2.1 ELK简介2.2 为什么要用ELK?2.3 ELK的组件 三、ELK组件详解3.1 Logstash3.1.1 简介3.1.2 Logstash命令常用选项3.1.3 Logstash 的输入和输出流3.1.4 Logstash配置文件 3.2 E…

客户开发信怎么写?新手如何发客户开发信?

客户开发信模板有哪些&#xff1f;编写外贸客户邮件的技巧&#xff1f; 客户开发信是一种重要的商业沟通工具&#xff0c;用于建立和维护与现有客户之间的联系&#xff0c;以及吸引新客户。写一封成功的客户开发信需要一定的技巧和策略。在这篇文章中&#xff0c;我们将介绍如…

数据结构:二叉树(2)

二叉树的基本操作 获取树的结点总数 遍历思路&#xff1a; 每次遍历一个节点&#xff0c;遍历完nodeSize&#xff0c;然后遍历它的左右子树 如果遍历到空的节点&#xff0c;就返回0 public int nodeSize 0;int size(TreeNode root){if(root null){return 0;}nodeSize;siz…

智慧公厕:探索未来城市环境卫生设施建设新标杆

智慧公厕是当代城市建设的一项重要举措&#xff0c;它集先进技术、人性化设计和智能管理于一体&#xff0c;为人们提供更为舒适、便捷和卫生的厕所环境。现代智慧公厕的功能异常丰富&#xff0c;从厕位监测到多媒体信息交互&#xff0c;从自动化清洁到环境调控&#xff0c;每一…

电商数据平台西域根据ID取商品详情API接口采集产品详情数据、价格 、销量数据操作指南

商品详情API接口是一种用于访问和获取商品信息的接口&#xff0c;通常用于连接电商平台和商家应用程序。这个接口可以提供有关商品的各种详细信息&#xff0c;如名称、价格、描述、图片、类别、库存和评价等。它使得开发者能够为平台上的消费者提供更个性化和定制化的购物体验&…

通达OA saas化改造

登录页增加选择单位下拉菜单 <tr height"37" class"login_field"><td align"center"><b>用户名</b> <input type"text" class"text" name"UNAME" size"15" onmouseover&qu…

配电房无人值守方案

随着科技的不断进步&#xff0c;许多传统需要人工操作和维护的领域逐渐被自动化和智能化方案所替代。配电房作为电力供应的核心部分&#xff0c;也面临着同样的变革。 力安科技电易云配电室无人值守监控系统以智能物联数据采集和智能物联管控"为关键&#xff0c;通过加…

家装家具经营小程序商城的作用是什么

无论富贫&#xff0c;家里家具总是不可少的&#xff0c;也是人们生活所需&#xff0c;随着人们生活质量提升&#xff0c;市场中各式家装家具品牌也非常多&#xff0c;琳琅满目的商场里价格/质量高低可供消费者自由选择。 随着线上电商快速崛起&#xff0c;由于线上具备多品牌汇…

接口自动化测试难点:数据库验证解决方案!

接口自动化中的数据库验证&#xff1a;确保数据的一致性和准确性 接口自动化测试是现代软件开发中不可或缺的一环&#xff0c;而数据库验证则是确保接口返回数据与数据库中的数据一致性的重要步骤。本文将介绍接口自动化中的数据库验证的原理、步骤以及示例代码&#xff0c;帮…

数字电路设计得力助手——《Design Compiler User Guide》

在当今数字化时代&#xff0c;电子设备和芯片的需求日益增长&#xff0c;这使得数字电路设计变得愈发重要。在数字电路设计过程中&#xff0c;使用先进的工具和技术是至关重要的。Synopsys公司的Design Compiler就是这样一款备受推崇的设计编译器软件&#xff0c;而其详尽的用户…

Android DI框架-Hilt

到底该如何理解<依赖注入> 模版代码&#xff1a;食之无味&#xff0c;弃之可惜 public class MainActivity extends Activity {Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);TextView mTextView(TextView) findVi…