【线程池总结】

文章目录

  • 线程池介绍
  • 线程池的优点
  • 线程池的执行流程
  • 线程池池参数:
  • 线程池的状态
  • 常见的线程池
    • FixedThreadPool(有限线程数的线程池):
    • ScheduledThreadPool(定时线程池)
    • scheduleWithFixedDelay
    • SingleThreadExecutor(单一线程池)
    • SingleThreadScheduledExecutor(单一定时线程池)

线程池介绍

线程池是一种线程管理的机制,它可以在应用程序中重复使用固定数量的线程,以减少线程创建和销毁的开销,提高资源利用率,并且可以控制并发线程数量,防止资源耗尽和系统崩溃。
在 Java 中,线程池通常使用 java.util.concurrent 包中的 ThreadPoolExecutor 类来实现。通过 ThreadPoolExecutor,你可以创建一个线程池,并指定线程池的大小、任务队列、拒绝策略等。

线程池的优点

“在线程池中执行任务”比“为每个线程分配一个任务”优势更多。通过重用现有的线程而不是创建线程,可以在处理多个请求时分摊在线程创建和销毁过程中产生的巨大开销。另一个额外的好处是,当请求到达时,工作线程通常已经存在,因此不会由于等待创建线程而延迟任务的执行,从而提高了响应性。通过适当的调整线程池的大小,可以创建足够的线程以便使处理器保持忙碌状态,同时还可以防止过多线程相互竞争资源而使应用程序耗尽内存或失败。

线程池的执行流程

在这里插入图片描述

  1. 提交一个新线程任务,线程池会在线程池中分配一个空闲线程,用于执行线程任务;
  2. 如果线程池中不存在空闲线程,则线程池会判断当前“存活的线程数”是否小于核心线程数corePoolSize。
    3.如果小于核心线程数corePoolSize,线程池会创建一个新线程(核心线程)去处理新线程任务;
    4.如果大于核心线程数corePoolSize,线程池会检查工作队列;
    5.如果工作队列未满,则将该线程任务放入工作队列进行等待。线程池中如果出现空闲线程,将从工作队列中按照FIFO的规则取出1个线程任务并分配执行;
    6.如果工作队列已满,则判断线程数是否达到最大线程数maximumPoolSize;
    如果当前“存活线程数”没有达到最大线程数maximumPoolSize,则创建7.一个新线程(非核心线程)执行新线程任务;
    8.如果当前“存活线程数”已经达到最大线程数maximumPoolSize,直接采用拒绝策略处理新线程任务;
    综上所述,执行顺序为:核心线程、工作队列、非核心线程、拒绝策略。

线程池池参数:

线程池的参数包括以下几个方面:
1.核心线程数(Core Pool Size):线程池中保持活动的最小线程数。即使线程是空闲的,核心线程也不会被回收。当有新的任务提交时,线程池会创建新的线程,直到达到核心线程数为止。
2.最大线程数(Maximum Pool Size):线程池中允许存在的最大线程数。当有新的任务提交时,如果线程池中的线程数量小于最大线程数,则会创建新的线程来处理任务。如果线程数量已经达到最大线程数,后续的任务会被放入任务队列中等待执行。
3.任务队列(Work Queue):用于存放等待执行的任务的队列。当线程池中的线程数量达到核心线程数,并且任务队列已满时,后续的任务会根据拒绝策略来处理。
4.线程存活时间(Keep Alive Time):当线程池中的线程数量大于核心线程数时,空闲线程的最长存活时间。如果线程在空闲时间超过了这个值,就会被终止并从线程池中移除。
5.拒绝策略(Rejected Execution Handler):当线程池中的线程数量已经达到最大线程数,并且任务队列已满时,新提交的任务会根据拒绝策略来处理。常见的拒绝策略包括抛出异常、丢弃任务、阻塞调用者和丢弃队列中最旧的任务。
6.线程工厂

线程池的状态

RUNNING:
运行状态,线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0。该状态的线程池会接收新任务,并处理工作队列中的任务。
调用线程池的shutdown()方法,可以切换到SHUTDOWN关闭状态;
调用线程池的shutdownNow()方法,可以切换到STOP停止状态;
SHUTDOWN:
关闭状态,该状态的线程池不会接收新任务,但会处理工作队列中的任务;
当工作队列为空时,并且线程池中执行的任务也为空时,线程池进入TIDYING状态;
STOP:
停止状态,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行 的任务;
线程池中执行的任务为空,进入TIDYING状态;
TIDYING:
整理状态,该状态表明所有的任务已经运行终止,记录的任务数量为0;terminated()执行完毕,进入TERMINATED状态;
TERMINATED:
终止状态,该状态表示线程池彻底关闭。

常见的线程池

FixedThreadPool(有限线程数的线程池):

FixedThreadPool线程池的特点是他的核心线程数和最大线程数是一样的,你可以把它看作成是一个固定线程数的线程池,因为他不会去将超出线程数的线程缓存到队列中,如果超出线程数了,就会按线程拒绝策略来执行。可以看下面的示例代码

public class ThreadPoolMain {

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 20; i++) {
            executor.execute(()->{
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"执行了");
            });
        }
        executor.shutdown();
    }
}

ScheduledThreadPool(定时线程池)

这个线程就是为了定时而发明的,它支持定时或周期性执行任务,比如10秒钟执行一次任务。
实现的方法具体如下

public class ThreadPoolMain {

    public static void main(String[] args) {
        ScheduledExecutorService service  =  Executors.newScheduledThreadPool(10);

        Runnable task02 = new Runnable() {
            @Override
            public void run() {
                String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
                System.out.println(time+":scheduleAtFixedRate 开始执行");
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
                System.out.println(time+":scheduleAtFixedRate 执行完成了");

            }
        };

        String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));

        System.out.println(time+":开始执行");

        service.scheduleAtFixedRate(task02,  10,  10,  TimeUnit.SECONDS);

    }
}

这里我们可以看到,scheduleAtFixedRate设置了两个参数,一个是initialDelay,另外一个是period,initialDelay表示项目启动时,需要隔多久时间才开始执行第一次,period则表示后面继续执行的间隔时间。这里你会发现,scheduleAtFixedRate会严格执行定时时间来执行,他不会管之前正在执行的定时方法有没有执行完成,他还是会照样不变10秒执行一次。

scheduleWithFixedDelay

public class ThreadPoolMain {

    public static void main(String[] args) {
        ScheduledExecutorService service  =  Executors.newScheduledThreadPool(10);

        Runnable task03 = new Runnable() {
            @Override
            public void run() {
                String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
                System.out.println(time+":scheduleWithFixedDelay 开始执行");
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
                System.out.println(time+":scheduleWithFixedDelay 执行完成了");

            }
        };

        String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));

        System.out.println(time+":开始执行");

        service.scheduleWithFixedDelay(task03,  10,  10,  TimeUnit.SECONDS);

    }
}

scheduleWithFixedDelay方法其实和scheduleAtFixedRate方法很相像,但是他们有个区别,就是等不等的区别,像scheduleAtFixedRate方法,他不会等到之前的线程是否有没有执行完成,他照样还是会按照约定好的定时时间去执行任务,而scheduleWithFixedDelay不同,他会等待线程执行完成之后,上一次线程结束时间来开始计算。简单来说他们的区别就是scheduleAtFixedRate的执行周期是按照线程任务的开始执行时间,scheduleWithFixedDelay的执行周期是按照线程任务的结束时间。

SingleThreadExecutor(单一线程池)

这个线程很适用于需要按照提交顺序去执行线程的场景,因为在他的线程池中,只有一个线程可以执行,他的原理其实跟FixedThreadPool有点相像,只不过他的核心线程数和最大线程数都是1,这样当提交者去提交线程的时候,就必须先让线程池中的线程执行完成之后才会去执行接下来的线程。这样就保证了线程的顺序性,而这种顺序性,前面几种线程的机制是做不到的。

SingleThreadScheduledExecutor(单一定时线程池)

这个线程池像是SingleThreadExecutor和ScheduledThreadPool生的娃,他有SingleThreadExecutor单一线程池的特性,一次只能执行一个线程,又可以完成ScheduledThreadPool线程池的定时功能。如果你能理解这个线程服务的能力,你就能理解这个线程的能力。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

synchronized的底层原理

目录 介绍 实现原理 对象头 Monitor&#xff08;监视器&#xff09; 锁升级 偏向锁 轻量级锁 重量级锁 锁的优缺点 介绍 synchronized 是 Java 中的关键字&#xff0c;它用于锁定代码块或方法&#xff0c;以确保同一时刻只有一个线程可以进入被锁定的部分。这在多线程…

企商在线亮相2024中国生成式AI大会,展出多元异构算力服务

4月18—19日&#xff0c;由知名媒体机构智东西与智猩猩共同主办的2024中国生成式AI大会在北京举行&#xff0c;55位重量级产学研投界代表同台分享。企商在线作为算力行业代表企业&#xff0c;参展生成式AI展区&#xff0c;现场展出企商在线AI算力平台及异构算力服务。 大会以“…

NodeRed节点编辑用于边缘计算和规则引擎,能做带UI界面和业务逻辑的上位机或前端应用吗?

网站&#xff1a;hhtp://www.uiotos.net 先说结论&#xff0c;可以&#xff0c;但是需要有页面嵌套继承类似的技术&#xff0c;实现页面模块化封装&#xff0c;否则难以实现复杂应用。 相信目光敏锐的人都在关注节点编辑在自身行业的应用&#xff01; NodeRed在边缘计算做数据…

四、Flask进阶

Flask-Cache pip install flask-caching安装flask_cache初始化 # ext.py from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate from flask_caching import Cachedb SQLAlchemy() migrate Migrate() cache Cache(config{CACHE_TYPE: simple # 缓存…

debian配置distcc分布式编译

前言 distcc 是一个用于在网络上的多台机器上分发 C、C、Objective C 或 Objective C 代码构建的程序。 distcc 应始终生成与本地构建相同的结果&#xff0c;易于安装和使用&#xff0c;并且通常比本地编译快得多。 distcc 不要求所有机器共享文件系统、同步时钟或安装相同的…

编写一款2D CAD/CAM软件(十四)绘制工具栏

前面的文章已经封装了数个最基本的图元&#xff0c;但是视图的呈现是基于测试数据形成的。为了尽快完善软件交互的框架和能力&#xff0c;本文将增加工具栏。 资源文件 1.首先&#xff0c;创建按钮图标。使用绘图软件构建出工具栏按钮的图标&#xff0c;绘图软件多种多样&…

使用 Rust 后,我​​使用 Python 的方式发生了变化

使用 Rust 后&#xff0c;我​​使用 Python 的方式发生了变化 Using type hints where possible, and sticking to the classic “make illegal state unrepresentable” principle. 尽可能使用类型提示&#xff0c;并坚持经典的“使非法状态不可表示”原则。 近年来&#xff…

PyTorch Conv2d 前向传递中发生了什么?

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

决策大模型专题(一)

决策智能不应该停留在以前的思维中了&#xff0c;现在开一个专题来学习一下决策论坛的老师们的精彩的内容。本内容来自决策大模型论坛&#xff0c;张伟楠老师的内容整理。 决策大模型 是新一代人工智能的底层技术&#xff0c;它可以去赋能&#xff0c;智能体也就是AI agent&a…

C++进阶:map与set容器的使用

目录 1. 关联式容器map与set2. set与multiset的接口与使用2.1 set的接口与使用2.1.1 成员函数2.1.2 迭代器2.1.3 容量相关2.1.4 修改相关 2.1.5 查找&#xff0c;计数与补充2.2 multiset的接口与使用 3. map与multimap的接口与使用3.1 map的接口与使用3.1.1 map的使用补充3.1.2…

小孩子不懂事,写着玩的

目录 Web攻防 特有漏洞 ASP安全 ASPX&#xff08;.NET&#xff09;安全 PHP安全 JavaWeb安全 JS&#xff0c;Node.js安全 Java安全 Python安全 通用漏洞 SQL注入 MySQL-root高权限读写注入 PostgreSQL-高权限读写注入 MSSQL-sa高权限读写执行注入 SQL注入体系 o…

QWidget 类

QWidget 类中包括框架的属性 QWidget 类中不包括框架的属性 总结:可使用以下两种方法设置部件的位置和大小 ①、通常使用 move()设置部件的位置,使用 resize()设置部件的大小。 ②、使用 setGeometry()函数同时设置部件的位置和大小。 ③、无法为部件指定包含边框在内的大…

C语言操作符和关键字

文章目录 操作符单目操作符sizeof&#xff08;类型&#xff09;强制类型转换 关系操作符、逻辑操作符、条件操作符逗号表达式 常见关键字typedefstaticstatic修饰局部变量static修饰全局变量static修饰函数 register寄存器关键词define定义常量和宏 操作符 单目操作符 C语言中…

echarts bar图表实现多个label显示

2024.0.23今天我学习了使用bar组件&#xff0c;可以渲染多个label显示的效果&#xff0c;如&#xff1a; 当我们有一个这样的图表时&#xff0c;根据需求需要在 这上面的顶部再显示一个空置床位数占用床位数的合计总值&#xff0c;如果直接添加一个label肯定是不行&#xff0c;…

深度学习-线性代数

目录 标量向量矩阵特殊矩阵特征向量和特征值 标量由只有一个元素的张量表示将向量视为标量值组成的列表通过张量的索引来访问任一元素访问张量的长度只有一个轴的张量&#xff0c;形状只有一个元素通过指定两个分量m和n来创建一个形状为mn的矩阵矩阵的转置对称矩阵的转置逻辑运…

[MYSQL索引优化] 分页查询优化

这里一共介绍两种常见的分页索引优化技巧,let go! 示例表: CREATE TABLE t_product (id int(0) NOT NULL,pname varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,price double(7, 2) NULL DEFAULT 0.00,promoteSales varchar(200) CHARA…

Linux进程详解三:进程状态

文章目录 进程状态Linux下的进程状态运行态-R阻塞态浅度休眠-S深度睡眠-D暂停状态-T暂停状态-t 终止态僵尸-Z死亡-X 孤儿进程 进程状态 进程的状态&#xff0c;本质上就是一个整型变量&#xff0c;在task_struct中的一个整型变量。 状态的存在决定了你的后续行为动作。 Linu…

DRF: 序列化器、View、APIView、GenericAPIView、Mixin、ViewSet、ModelViewSet的源码解析

前言&#xff1a;还没有整理&#xff0c;后续有时间再整理&#xff0c;目前只是个人思路&#xff0c;文章较乱。 注意路径匹配的“/” 我们的url里面加了“/”&#xff0c;但是用apifox等非浏览器的工具发起请求时没有加“/”&#xff0c;而且还不是get请求&#xff0c;那么这…

C++字符串中单词的提取以及按符号分隔

句子中的单词提取利用stringstream即可 如果要分割需配合getline使用 两者前提都是要先转化为字符串流。

Linux套接字编程详解

Linux套接字编程 预备知识IP地址和MAC地址套接字结构网络字节序 UDP套接字编程服务端代码客服端代码 TCP 套接字守护进程 计算器模块1 日志头文件序列化和反序列化 预备知识 IP地址和MAC地址 MAC地址用来在局域网中标识唯一主机 Ip地址用于在广域网中标识唯一主机 &#xff0…