线程池的理解以及实现线程池

news2024/9/22 17:24:15

线程池可以干什么:

  • 帮助我们减少线程的创建和销毁
  • 提高系统资源的利用率,同时控制并发执行的线程数量
  • 便于管理且提高响应速度

线程池的工作流程:

1.创建线程池

线程池的创建是通过 Executors 工厂方法或直接使用 ThreadPoolExecutor 构造函数来完成

使用 Executors 创建线程池的⼏种⽅式

  • newFixedThreadPool: 创建固定线程数的线程池
  • newCachedThreadPool: 创建线程数⽬动态增⻓的线程池.
  • newSingleThreadExecutor: 创建只包含单个线程的线程池
  • newScheduledThreadPool: 设定 延迟时间后执⾏命令,或者定期执⾏命令. 是进阶版的 Timer. Executors 本质上是 ThreadPoolExecutor 类的封装.

 直接使用ThreadPoolExecutor 构造函数来完成,代码的大致效果就是下面这段代码:

//使用 ThreadPoolExecutor 构造函数
ThreadPoolExecutor executor = new ThreadPoolExecutor(
                corePoolSize,//核心线程数
                maximumPoolSize,//最大线程数
                keepAliveTime,//非核心线程数在空闲时存在的最长时间
                unit,//设置时间单位
                workQueue,//任务队列
                threadFactory,//线程工厂,可以自定义线程的创建过程,包括设置线程名称、优先级等(不是必须的)
                RejectedExecutionHandler///拒绝策略
        );

2.提交任务

一旦线程池创建完成,就可以向线程池提交任务。提交任务通常通过 ExecutorService 接口的 execute(Runnable command) 方法或使用 Lambda 表达式来完成。提交的任务会被封装成 RunnableCallable 对象。

使用 Lambda 表达式提交任务:

ExecutorService service = Executors.newFixedThreadPool(4);//创建一个固定大小为4的线程池
        service.execute(()->{
            System.out.println("你需要提交的任务");
        });

使用 Runnable 来提交任务:

ExecutorService service = Executors.newFixedThreadPool(4);//创建一个固定大小为4的线程池
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("你需要提交的任务");
            }
        });

3.检查当前线程数

  1. 如果正在运行的线程数小于corePoolSize (核心线程数)则创建新的线程来执行任务,即使当前线程池中有空闲线程
  2. 如果正在运行的线程数等于corePoolSize 且存在空闲线程,则使用空闲线程来执行任务
  3. 如果正在运行的线程数等于corePoolSize 但是没有空闲线程,则将任务加入任务队列中等待执行

4.检查任务队列

  1. 如果任务队列没有满,则将新提交的任务加入到队列中等待执行
  2. 如果队列已满,则根据当前线程数与 maximumPoolSize (最大线程数) 来判断:
  • 如果当前线程数小于maximumPoolSize,则创建新的线程来执行任务
  • 如果当前线程数等于maximumPoolSize 且任务队列已满,则触发拒绝策略
  • 如果当前线程数等于maximumPoolSize 但是任务队列没满,则将新的任务放入任务队列等待执行

5.执行拒绝策略

如果线程池无法接受更多的任务(即线程数量达到最大值且任务队列已满),则会根据RejectedExecutionHandler 实现来处理无法执行的任务

  • AbortPolicy: 抛出 RejectedExecutionException 异常。
  • CallerRunsPolicy: 由调用者线程执行新任务。(当前任务比较重要又不能被丢弃的时候就可以使用这个,但是可能会增加响应时间)
  • DiscardOldestPolicy: 丢弃队列中最老的任务。
  • DiscardPolicy: 丢弃新来的任务。

6.空闲线程的管理

我们可以理解成核心线程(corePoolSize)是公司里面的正式员工,非核心线程数(超过corePoolSize的部分)是公司里面的实习生,假设最大空闲时间(KeepAliveTime)为一个月,如果这一个月内正式员工没有任何工作(处于空闲状态),那么这种情况下是不会轻易被裁掉的(销毁),但是如果是实习生连续一个月没有工作,那么这种情况下是会面临被裁掉的风险(销毁)用于节约成本(资源)

如果非核心线程数大于 corePoolSize 且线程空闲时间超过 keepAliveTime ,则线程会被销毁,直到线程数等于 corePoolSize

7.关闭线程池

可以调用 ExecutorServiceshutdown() 方法来关闭线程池。这将阻止新的任务提交,但允许正在执行的任务完成。如果需要立即终止所有任务并关闭线程池,可以使用 shutdownNow() 方法。


代码实现线程池

使用 Executors 类工厂方法

newSingleThreadExecutor()

作用

  • 创建一个单线程化的 ExecutorService
  • 保证所有任务按照提交顺序执行,一次只执行一个任务

默认配置

  • corePoolSize: 1
  • maximumPoolSize: 1
  • keepAliveTime: 无意义(因为只有一个线程,且总是活跃的)
  • workQueueLinkedBlockingQueue(无界队列)
  • threadFactoryExecutors.defaultThreadFactory()
  • RejectedExecutionHandlerAbortPolicy()

代码示例:

public static void main(String[] args) {
        // 创建一个单线程的线程池
        ExecutorService service = Executors.newSingleThreadExecutor();

        //提交任务到线程池
        for(int i = 0;i<5;i++){
            final int id = i;
            service.submit(()->{
                System.out.println(Thread.currentThread().getName()+"正在执行任务"+id);
            });
        }
    }

运行结果:


newFixedThreadPool(int nThreads) 

作用:

  • 创建一个固定大小的线程池。
  • 线程池中的线程数量是固定的,一旦创建,线程池中的线程数量不会改变。
  • 适用于处理大量短期任务,且希望限制线程数量的情况。

默认配置

  • corePoolSizenThreads
  • maximumPoolSizenThreads
  • keepAliveTime: 无意义(因为线程池大小固定)
  • workQueueLinkedBlockingQueue(无界队列)
  • threadFactoryExecutors.defaultThreadFactory()
  • RejectedExecutionHandlerAbortPolicy()

代码示例:

public static void main(String[] args) {
        //创建一个固定大小为6的线程池
        ExecutorService service = Executors.newFixedThreadPool(6);
        
        //提交任务到线程池
        for(int i = 0;i<10;i++){
            final int id = i;
            service.submit(()->{
                System.out.println(Thread.currentThread().getName()+"正在执行任务"+id);
            });
        }
    }

运行结果:


newCachedThreadPool() 

作用:

  • 创建一个可以根据需要创建新线程的线程池。
  • 线程池会根据需要创建新线程,但会在任务完成后销毁空闲线程。
  • 适用于执行很多短期异步任务的场合。

默认配置

  • corePoolSize: 0
  • maximumPoolSizeInteger.MAX_VALUE
  • keepAliveTime: 60秒
  • workQueueSynchronousQueue(一个特殊的无界队列,队列本身不存储元素,每个插入操作必须等待另一个线程的移除操作)
  • threadFactoryExecutors.defaultThreadFactory()
  • RejectedExecutionHandlerAbortPolicy()

代码示例:

// 创建一个可缓存的线程池
        ExecutorService service = Executors.newCachedThreadPool();

        for(int i = 0;i<10;i++){
            final int id = i;
            service.submit(()->{
                System.out.println(Thread.currentThread().getName()+"正在执行任务"+id);
            });
        }

运行结果:


newScheduledThreadPool(int corePoolSize) 

作用:

  • 创建一个可以安排定期或延迟任务执行的线程池。
  • 线程池中的线程数量是固定的,可以根据需要扩展到 corePoolSize 的数量。
  • 适用于需要定期执行任务的场景。

默认配置

  • corePoolSizecorePoolSize
  • maximumPoolSizeInteger.MAX_VALUE
  • keepAliveTime: 无意义(因为线程池大小固定)
  • workQueueDelayedWorkQueue(一个特殊类型的队列,用于存放延迟任务)
  • threadFactoryExecutors.defaultThreadFactory()
  • RejectedExecutionHandlerAbortPolicy()

代码示例:

public static void main(String[] args) {
        //核心线程数为3
        ScheduledExecutorService service = Executors.newScheduledThreadPool(3);

        //给定的任务在指定延迟后执行一次
        service.schedule(()->{
            System.out.println("任务1执行");
        },5, TimeUnit.SECONDS);
        //每隔2秒执行一次,初始延迟0秒
        service.scheduleAtFixedRate(()->{
            System.out.println("周期性任务开始执行");
        },0,2,TimeUnit.SECONDS);
    }

运行结果:

使用 ThreadPoolExecutor 构造函数

演示一个简单的

public static void main(String[] args) {
        // 创建线程池
        ExecutorService executor = new ThreadPoolExecutor(
            2, // corePoolSize: 2 个正式员工
            5, // maximumPoolSize: 最多5个线程(2个正式员工 + 3个临时工)
            60, // keepAliveTime: 临时工空闲60秒后被销毁
            TimeUnit.SECONDS, // 时间单位为秒
            new ArrayBlockingQueue<>(10), // workQueue: 阻塞队列,最多容纳10个任务
            Executors.defaultThreadFactory(), // threadFactory: 使用默认的线程工厂
            new ThreadPoolExecutor.CallerRunsPolicy() // RejectedExecutionHandler: 由调用者线程执行新任务
        );
    }

自己实现一个简单的线程池

这么这段代码没有任何过多的考虑,只是单纯的实现了一个线程池

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

class MyThreadPool{
    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();

    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }

    public MyThreadPool(int n){
        for(int i = 0;i<n;i++){
            Thread t = new Thread(()->{
                while(true){
                    try {
                        Runnable runnable = queue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            t.start();
        }
    }
}
public class demo1 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool pool = new MyThreadPool(4);
        for(int i = 0;i<1000;i++){
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+" hello");
                }
            });
        }
    }
}

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

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

相关文章

遇到Websocket就不会测了?别慌,学会这个Jmeter插件轻松解决....

websocket 是一种双向通信协议&#xff0c;在建立连接后&#xff0c;websocket服务端和客户端都能主动向对方发送或者接收数据&#xff0c;而在http协议中&#xff0c;一个request只能有一个response&#xff0c;而且这个response也是被动的&#xff0c;不能主动发起。 websoc…

C语言 ——— 数组指针的定义 数组指针的使用

目录 前言 数组指针的定义 数组指针的使用 前言 之前有编写过关于 指针数组 的相关知识 C语言 ——— 指针数组 & 指针数组模拟二维整型数组-CSDN博客 指针数组 顾名思义就是 存放指针的数组 那什么是数组指针呢&#xff1f; 数组指针的定义 何为数组指针&#xf…

FastAPI(七十七)实战开发《在线课程学习系统》接口开发-- 课程编辑和查看评论

源码见&#xff1a;"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 课程编辑 先来看下课程编辑 1.判断是否登录 2.判断课程是否存在 3.是否有权限&#xff08;只有自己可以修改自己的课程&#xff09; 4.名称是否重复…

Redis的操作以及SpringCache框架

目录 一.什么是Redis&#xff1f; 二.Redis的相关知识&#xff1a; 三.如何操作Redis&#xff1f; 1&#xff0c;常用命令&#xff1a; 2.Spring Data Redis &#xff08;1&#xff09; pom.xml 配置&#xff1a; &#xff08;2&#xff09;配置Redis数据源&#xff1a; …

IE11添加收藏、关闭窗口时弹出的对话框字体又大又粗很难看的解决办法

原因已查明&#xff0c;在win7 sp1 32位系统下&#xff0c;安装“2020-01 适用于基于 x86 的系统的 Windows 7 月度安全质量汇总&#xff08;KB4534310&#xff09;”这个更新会导致IE11的窗口字体变大变粗&#xff0c;把这个更新卸载了就可以了&#xff0c;无需重装IE11浏览器…

【芯智雲城】详解智能电机驱动在汽车中的应用

随着汽车系统中传统的机械化设计被电子化设计逐渐取代&#xff0c;电机在汽车电子化系统中扮演的角色越来越重要。例如在汽车的动力系统中&#xff0c;由传统的燃油发动机逐步发展为现在的有刷同步电机、感应电机&#xff0c;真正的实现了新能源车的动力革命&#xff1b;传统的…

论文快过(图像配准|Coarse_LoFTR_TRT)|适用于移动端的LoFTR算法的改进分析 1060显卡上45fps

项目地址&#xff1a;https://github.com/Kolkir/Coarse_LoFTR_TRT 创建时间&#xff1a;2022年 相关训练数据&#xff1a;BlendedMVS LoFTR [19]是一种有效的深度学习方法&#xff0c;可以在图像对上寻找合适的局部特征匹配。本文报道了该方法在低计算性能和有限内存条件下的…

Android AutoSize屏幕适配:适配不同屏幕大小的尺寸,让我们无需去建立多个尺寸资源文件

目录 AutoSize是什么 AutoSize如何使用 一、AndroidautoSize是什么 在开发产品的时候&#xff0c;我们会遇到各种各样尺寸的屏幕&#xff0c;如果只使用一种尺寸去定义控件、文字的大小&#xff0c;那么到时候改起来就头皮发麻。以前使用dime的各种类库&#xff0c;文件太多…

一种提供改进的通道迁移率和高可靠性的SiC沟槽MOSFET概念

来源&#xff1a;A SiC Trench MOSFET concept offering improved channel mobility and high reliability&#xff08;2017 19th European Conference on Power Electronics and Applications (EPE’17 ECCE Europe)&#xff09; 摘要 这项工作讨论了与硅基同类产品相比&…

性能测试工具 - Siege

在快速发展的技术时代&#xff0c;网站和应用的性能对于用户体验和业务成功至关重要。作为测试工程师&#xff0c;找到高效的性能测试工具显得尤为重要。今天&#xff0c;我们来聊聊一个备受推崇的性能测试工具——Siege。 为什么Siege能够在众多性能测试工具中脱颖而出&#x…

C++ 内存与编译问题总结

目录 C内存结构 作用域与生存周期 堆与栈 内存对齐 智能指针 shared_ptr 循环引用问题 编译与链接 内存泄漏 补充问题 include “ ”与<> 大端与小端 C内存结构 C程序内存分区 代码区 文件中所有的函数代码、常量以及字符串常量只读&#xff0c;保护程序不会被…

在invidia jetpack4.5.1上运行c++版yolov8(tensorRT)

心路历程(可略过) 为了能在arm64上跑通yolov8,我试过很多很多代码,太多对库版本的要求太高了; 比如说有一个是需要依赖onnx库的,(https://github.com/UNeedCryDear/yolov8-opencv-onnxruntime-cpp) 运行成功了报错error: IOrtSessionOptionsAppendExecutionProvider C…

力扣高频SQL 50题(基础版)第十八题

文章目录 力扣高频SQL 50题&#xff08;基础版&#xff09;第十八题1633. 各赛事的用户注册率题目说明思路分析实现过程准备数据实现方式结果截图 力扣高频SQL 50题&#xff08;基础版&#xff09;第十八题 1633. 各赛事的用户注册率 题目说明 用户表&#xff1a; Users --…

嵌入式Python、ROS、SLAM、WebSocket和Node.js:智能巡逻监控安防机器人设计流程(代码示例)

项目概述 随着智能技术的发展&#xff0c;智能巡逻机器人在安防、监控和巡逻等领域的应用越来越广泛。本文将介绍一个结合嵌入式系统、机器人技术和后端开发的智能巡逻机器人。该机器人能够自主导航&#xff0c;实时检测异常情况&#xff08;如火灾或入侵者&#xff09;&#…

免费【2024】springboot 超市在线销售系统的设计与实现

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

Adobe Photoshop(Ps)安装包软件下载

一、Adobe Photoshop简介 Adobe Photoshop&#xff08;简称PS&#xff09;是由Adobe Systems公司开发的图像处理软件&#xff0c;它是一款集图像扫描、编辑修改、图像制作、广告创意、图像输入与输出于一体的图形图像处理软件。广泛应用于专业测评、平面设计、广告摄影、影像创…

通过限制访问,实现纯私有Docker镜像

怎么会不过审呢?没有敏感信息呀。 For obvious reasons,Many Docker image repositories are inaccessible,The official warehouse has also been filtered by the firewall,So write about how to build a self use Docker image using CloudFlares Workers and Pages. …

SQL Server 设置端口号:详细步骤与注意事项

目录 一、了解SQL Server端口号的基础知识 1.1 默认端口号 1.2 静态端口与动态端口 二、使用SQL Server配置管理器设置端口号 2.1 打开SQL Server配置管理器 2.2 定位到SQL Server网络配置 2.3 修改TCP/IP属性 2.4 重启SQL Server服务 三、注意事项 3.1 防火墙设置 3…

VSCode 解决 pylint 报错 No name QWidget in module PyQt5.QtWidgets

问题 启用了 VSCode 的 Pylint 插件, 即便 Python 环境中安装了 PyQt5, 也无法正确解析 PyQt5 的导入 PyQt5 底层代码是用 C/C 写的, pylint 默认不会深入解析 pylint doesn’t load any C extensions by default, because those can run arbitrary code. 解决 修改 Settings…

Internet Download Manager(IDM)2024中文版本有哪些新功能?6.42版本功能介绍

1. Internet Download Manager&#xff08;IDM&#xff09;是一款功能强大的下载管理器&#xff0c;支持所有流行的浏览器&#xff0c;并可提升下载速度高达5倍。 2. IDM具有智能下载逻辑加速器&#xff0c;可以设置文件下载优先级、分块下载等&#xff0c;提高下载效率。 IDM…