一文搞懂Java线程池执行原理核心参数

news2025/5/15 16:19:57

文章目录

  • 前言
  • 一、为什么要使用线程池创建线程?
  • 二、线程池的核心参数(重点)
    • 1.核心线程数
    • 2.最大线程数
    • 3.救急线程的存活时间
    • 4.救急线程的时间单位
    • 5.任务队列
    • 6.线程工厂
    • 7.任务拒绝策略
  • 三、线程池的执行原理
  • 四、一个小案例
  • 总结


前言

最近面试过程中经常被问到多线程方面的问题,尤其是线程池,因为这也是工作中常用的创建线程的方式,本篇博客在于记录自己对于线程池的执行原理及如何创建线程池去执行任务的一个理解,同时方便后期回顾复习


一、为什么要使用线程池创建线程?

我总结了两方面原因:

  • 1.每次创建线程都需要占用一定的内存空间,如果无限地去创建线程可能会浪费内存空间,严重的话会导致内存溢出
  • 2.我们的CPU资源是有限的,同一时刻一个CPU只能处理一个线程,如果有大量的请求进来了,创建了大量的线程,很多线程是没有CPU执行权的,那么这些线程都得等待,会造成大量的线程之间的切换,同时也会导致性能变慢

二、线程池的核心参数(重点)

在这里插入图片描述
以ThreadPoolExecutor为例,我们可以看到它的构造函数,有7个核心参数,接下来,我会对它们做一个简单的解释

1.核心线程数

  • 就是说任务进来之后,线程池首先创建核心线程来执行任务,比如有两个任务进来,如果定义了核心线程数为2,则会创建2个核心线程来执行任务,任务再进来的话,就会进入阻塞队列

2.最大线程数

  • 最大线程数 = 核心线程数 + 救急线程数,如果核心线程数为2,最大线程数为3,那么救急线程数就是1

3.救急线程的存活时间

  • 意思是说,如果在救急线程存活时间内没有任务的话,救急线程就会被释放,但是核心线程是不会被释放的

4.救急线程的时间单位

  • 就是一个TimeUnit枚举类,它定义了几种时间单位:NANOSECONDS(纳秒)、MICROSECONDS(微秒)、MILLISECONDS(毫秒)、SECONDS(秒)、MINUTES(分)、HOURS(时)、DAYS(天)

5.任务队列

  • 任务阻塞队列,当新任务进来的时候,如果此时没有空闲核心线程去执行,那么任务就会先进入到任务阻塞队列中进行等待

6.线程工厂

  • 一般用来定义线程的名字

7.任务拒绝策略

  • 阻塞队列已经满了,也没有空闲的救急线程,此时如果新进入一个任务的话,那么会按照任务拒绝策略对新任务进行处理,有四种任务拒绝策略:
  • 1.AbortPolicy: 直接抛出异常
  • 2.CallerRunsPolicy: 用调用者所在的线程来执行任务,一般就是主线程
  • 3.DiscardOldestPolicy:丢弃阻塞队列中最靠前的任务,并执行当前任务
  • 4.DiscardPolicy:直接丢弃任务

三、线程池的执行原理

通俗的说,当新任务被提交的时候,判断核心线程数是否已满,如果没有则创建核心线程执行任务,如果核心线程数已满,则将新任务添加到阻塞队列中,若阻塞队列也满了,则判断当前线程数是否小于最大线程数,如果小于则创建救急线程来执行任务,如果当前线程数等于最大线程数,表示没有空闲线程来执行任务了,只能走拒绝策略,新任务就会根据拒绝策略进行处理。
在这里插入图片描述

四、一个小案例

详细代码:


```java
public class TestThreadPoolExecutor {

    static class MyTask implements Runnable {
        private final String name;
        private final long duration;

        public MyTask(String name) {
            this(name, 0);
        }

        public MyTask(String name, long duration) {
            this.name = name;
            this.duration = duration;
        }

        @Override
        public void run() {
            try {
                System.out.println("MyThread...running..." + this);
                Thread.sleep(duration);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        @Override
        public String toString() {
            return "MyTask(" + name + ")";
        }
    }

    public static void main(String[] args) throws InterruptedException {
        AtomicInteger c = new AtomicInteger(1);
        ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);

        LinkedBlockingQueue linkedBlockingQueue = new LinkedBlockingQueue();
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                2,
                3,
                0,
                TimeUnit.MILLISECONDS,
                queue,
                r -> new Thread(r, "myThread" + c.getAndIncrement()),
                new ThreadPoolExecutor.AbortPolicy());
        showState(queue, threadPool);
        threadPool.submit(new MyTask("1", 3600000));
        showState(queue, threadPool);
        threadPool.submit(new MyTask("2", 3600000));
        showState(queue, threadPool);
//        threadPool.submit(new MyTask("3"));
//        showState(queue, threadPool);
//        threadPool.submit(new MyTask("4"));
//        showState(queue, threadPool);
//        threadPool.submit(new MyTask("5",3600000));
//        showState(queue, threadPool);
//        threadPool.submit(new MyTask("6"));
//        showState(queue, threadPool);
    }

    private static void showState(ArrayBlockingQueue<Runnable> queue, ThreadPoolExecutor threadPool) {
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        List<Object> tasks = new ArrayList<>();
        for (Runnable runnable : queue) {
            try {
                Field callable = FutureTask.class.getDeclaredField("callable");
                callable.setAccessible(true);
                Object adapter = callable.get(runnable);
                Class<?> clazz = Class.forName("java.util.concurrent.Executors$RunnableAdapter");
                Field task = clazz.getDeclaredField("task");
                task.setAccessible(true);
                Object o = task.get(adapter);
                tasks.add(o);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("pool size:" + threadPool.getPoolSize() + ", " + tasks);
    }
}

在面代码中,我们定义了一个静态内部类,用来创建任务,在主方法中定义了线程池,以及它的一些核心参数,比如定义的核心线程数是2,最大线程数是3,那么救急线程数就是1了,救急线程的存活时间是0ms,任务阻塞队列是ArrayBlockingQueue,线程工厂定义了线程的名称,以及任务拒绝策略定义了AbortPolicy,当新任务来的时候,如果当前线程数等于最大线程,同时任务队列也满了的话,就会触发拒绝策略。
当我们只提交两个任务的时候,会创建两个核心线程去执行任务,此时阻塞队列是空的,运行结果如下:
在这里插入图片描述
当我们提交三个任务的时候,第三个任务会进入到阻塞队列:
在这里插入图片描述
在这里插入图片描述
当我们提交四个任务,第三个和第四个任务会进入阻塞队列
在这里插入图片描述
在这里插入图片描述
如果我们提交5个任务,由于阻塞队列我们一开始定义的容量是2,那么第五个任务就会创建救急线程去执行
在这里插入图片描述

在这里插入图片描述
当我们提交第六个任务的时候,由于阻塞队列满了,而且当前线程数也已经达到了最大线程数,此时就会触发任务拒绝策略,就会抛出异常:
在这里插入图片描述
在这里插入图片描述

总结

这篇博客简单说了一下线程池的核心参数和执行原理,以及用一个小案例说明它的一个工作流程,后面会继续更新线程池方面的知识点,比如有哪些阻塞队列,它们内部的原理是什么。

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

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

相关文章

CEC2018动态多目标优化算法:基于自适应启动策略的混合交叉动态约束多目标优化算法(MC-DCMOEA)求解CEC2018

一、动态多目标优化问题 1.1问题定义 1.2 动态支配关系定义 二、 基于自适应启动策略的混合交叉动态多目标优化算法 基于自适应启动策略的混合交叉动态多目标优化算法&#xff08;Mixture Crossover Dynamic Constrained Multi-objective Evolutionary Algorithm Based on Se…

Blender基础入门(1):Blender建模系统简单介绍

文章目录 我个人的Blender专栏前言偏好设置推荐常用组合按键空格&#xff08;需在偏好设置里面选择空格->工具&#xff09;ShiftA&#xff08;添加物体&#xff09;右键&#xff1a;物体对象操作I:显示关键帧操作~&#xff08;1左边的波浪号&#xff09;&#xff1a;视角选择…

【算法与数据结构】1047、LeetCode删除字符串中的所有相邻重复项

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;这道题和【算法与数据结构】20、LeetCode有效的括号类似&#xff0c;本质上都是找一个字符的匹配字符。…

深入理解Linux网络——内核是如何接收到网络包的

文章目录 一、相关实际问题二、数据是如何从网卡到协议栈的1、Linux网络收包总览2、Linux启动1&#xff09;创建ksotfirqd内核线程2&#xff09;网络子系统初始化3&#xff09;协议栈注册4&#xff09;网卡驱动初始化5&#xff09;网卡启动 3、迎接数据的到来1&#xff09;硬中…

Python潮流周刊#10:Twitter 的强敌 Threads 是用 Python 开发的!

△点击上方“Python猫”关注 &#xff0c;回复“1”领取电子书 你好&#xff0c;我是猫哥。这里每周分享优质的 Python 及通用技术内容&#xff0c;大部分为英文&#xff0c;已在小标题注明。&#xff08;标题取自其中一则分享&#xff0c;不代表全部内容都是该主题&#xff0c…

Vue+elementUI实现下拉框多选和反选

Vue代码如下&#xff1a; <el-form-item label"下拉框名称&#xff1a;"><el-select size"mini" v-model"testModelName" focus"getSelectInfo" :disabled"SelectStyle" filterable clearable placeholder"&…

SpringBoot实战(二十)集成Druid连接池

目录 一、简介1.定义2.特点3.竞品对比 二、搭建测试项目1.Maven依赖2.yaml配置2.1 JDBC配置2.2 连接池配置2.3 监控配置 三、测试1.查看监控页面2.单元测试 四、补充&#xff1a;1.如何打印慢SQL&#xff1f;2.去除广告3.如何手动获取监控内容 一、简介 1.定义 Druid数据库连…

简析电力系统网络靶场建设的价值、挑战与趋势

在当下已经演变为持久战的俄乌地区冲突中&#xff0c;通信、交通、能源供应等相关国家关键基础设施一直是双方互相攻击的重点目标。同时&#xff0c;“网络战”作为先行战场&#xff0c;也把关基设施作为主阵地&#xff0c;不断以相对轻量级成本制造比想象中更广泛的破坏和社会…

Haproxy搭建Web群集和脑裂的概念

目录 脑裂概念 脑裂如何生的: 解决方法 Haproxy概念 HAProxy的主要特性有&#xff1a; HAProxy负载均衡策略非常多&#xff0c;常见的有如下8种&#xff1a; CDN Nginx LVS haproxy haproxy服务器部署 关闭防火墙 编译安装 Haproxy Haproxy服务器配置 添加haprox…

Spring MVC教程

Spring MVC属于SpringFrameWork的后续产品&#xff0c;已经融合在Spring Web Flow里面。 Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。 使用 Spring 可插入的 MVC 架构&#xff0c;从而在使用Spring进行WEB开发时&#xff0c;可以选择使用Spring的Spring MVC框架或集…

明明都是2000坐标的地形图,怎么位置就不一样呢?

前几天有一个同事在工作中遇到一个问题,核心说起来就是,甲方发来两个年份的同一个片区的地形图,并且言之凿凿都是2000坐标的,但是在一个CAD复制后,到另一个CAD中使用“粘贴到原坐标”,就是位置对不上。 以下使用测试数据还原一下情景。 地形图A,是一个高程点: 地形图…

【网络系统集成】Windows Server集群实验

1.实验名称 Windows Server集群实验 2.实验目的 通过使用Windows 2003进行实验,理解与掌握服务器技术与系统集成相关知识点。 3.实验内容 (1)在Vmware Workstation中安装Windows Server 2003 (2)在Windows Server 2008中完成DNS,WEB的配置

LinuxI2C应用编程——访问EEPROM

文章目录 介绍读芯片手册代码编译运行 阅读博文&#xff1a;LinuxI2C应用编程——I2C-Tools的使用 介绍 EEPROM (Electrically Erasable Programmable read only memory)&#xff0c;指带电可擦可编程只读存储器。是一种掉电后数据不丢失的存储芯片。 读芯片手册 首先按如图…

SSMP整合案例(15) 解决分页中删除最后一页的最后一条数据,导致查询错位问题

上文 我们还是做了个比较重要的是 将我们的查询全部逻辑改为了分页查询 但是 目前 我们的删除和分页配合起来 它会有一点点问题 例如 这种情况 我们最后一页只有一条数据了 我们操作删除 将这条数据给他干掉 删除完之后 它会调分页查询 但我们当前页的条件还是之前的 例如 我…

【网络】UDP协议详解

目录 UDP的感性理解 UDP协议格式 UDP协议格式感性理解 UDP特点 UDP的缓冲区 UDP的感性理解 UDP的传输过程类似于寄信&#xff0c;假设你要写一封家书寄回家里&#xff1a;首先你要在信封上填写好寄件人和收件人的地址&#xff0c;其次在贴好邮票&#xff0c;最后将信件投放…

MySQL数据库 库表操作

1. (1) mysql> create database Market; 创建数据库 mysql> use Market 使用Market数据库(2) mysql> create table customers(-> c_num int(11) primary key auto_increment,-> c_name varchar(50),-> c_contact varchar(50),-> c_city varc…

ChatGPT炒股:从巨潮资讯网上批量下载特定主题的股票公告

巨潮资讯网是股票公告的指定披露渠道之一&#xff0c;上面有非常详细的A股股票公告内容。 现在&#xff0c;我们要获取2023-01-04~2023-07-04期间所有新三板公司中标题包含“2023年日常性关联交易”的公告。 首先从network中获取到真实网址&#xff1a;http://www.cninfo.com…

大一下学期期末考wp

【web】 1.sign 打开题目 发现有1000个页面 打开第9999个和第9998个页面&#xff0c;发现是utf-8编码 当再随机打开其他页面时&#xff0c;页面又出现了另外一种情况 于是我们猜测&#xff0c;flag是由utf-8编码的&#xff0c;编码被拆散了随机放在10000个页面中的几个页面中…

找不到msvcp140.dll解决方法有哪些?那个修复方法更简单

是使用Windows操作系统的计算机时&#xff0c;总是不可避免会遇到系统报错。像计算机提示找不到msvcp140.dll&#xff0c;msvcp140.dll是一个Windows操作系统中的动态链接库文件&#xff0c;它属于Microsoft Visual C Redistributable包的一部分。这个文件包含了一些供C程序使用…

计算机的大脑 CPU

晶体管 N型MOS管P型MOS管 算术逻辑单元 ALU 晶体管–>门电路–>加法器–>ALU 既可以做逻辑运算、也可以做逻辑运算、成为计算机CPU中非常核心的组件。 指令 一条指令只完成一个基本操作的精简指令集 RISC 、它们的指令长度基本上是固定的。比如 ARM一条指令可以…