多线程的案例之线程池 [Java EE 多线程]

news/2025/2/25 22:11:53

目录

1. 什么是线程池

1.1 定义

1.2 工作原理

1.3 优点

1.4 举例

2. 标准库中的线程池

2.1 创建线程池

2.2 Executors 创建线程池的几种方式

2.3 ThreadPoolExecutor 提供了更多的可选参数, 可以进一步细化线程池行为的设定

3. 实现线程池

3.1 主要操作说明

3.2 代码实现


1. 什么是线程池

1.1 定义

// 线程池(Thread Pool)是一种多线程处理形式,它预先创建并管理一定数量的线程,这些线程处于等待任务的状态。当有新的任务提交时,线程池会从池中选择一个空闲线程来执行该任务;若线程池内没有空闲线程,任务会被放入等待队列,等有线程空闲时再执行;若等待队列也满了,可能会根据预设策略处理,如拒绝任务或创建新线程(在允许的最大线程数范围内)。

1.2 工作原理

// 线程创建与初始化:在创建线程池时,会根据配置参数创建一定数量的线程,这些线程在创建后进入就绪状态,等待接收任务。

// 任务提交:当有新任务到达时,线程池会将任务分配给空闲线程。如果所有线程都在忙碌,任务会被存储在任务队列中。

// 任务执行:空闲线程从任务队列中获取任务并执行。执行完成后,线程不会销毁,而是重新回到空闲状态,等待下一个任务。

// 线程管理:线程池会根据系统的负载情况和配置参数,动态地管理线程的数量。例如,当任务较多时,可能会创建新的线程;当任务较少时,会销毁一些空闲线程以节省资源。

1.3 优点

// 降低资源消耗:避免了频繁创建和销毁线程所带来的开销。创建和销毁线程需要操作系统进行一系列的操作,如分配和回收内存、更新线程调度表等,这些操作会消耗大量的系统资源。使用线程池可以复用已有的线程,减少了这些开销。

// 提高响应速度:由于线程池中的线程已经预先创建好,当有任务提交时,无需等待线程的创建过程,可立即开始执行任务,从而提高了系统的响应速度。

// 便于线程管理:线程池可以对线程进行集中管理,如设置线程的最大数量、最小数量、线程的优先级等。通过合理配置线程池的参数,可以有效地控制并发线程的数量,避免因线程过多导致系统资源耗尽或性能下降。

1.4 举例

// 线程池就相当于是在原本的基础上多创建了一个备用路径,根据任务的多少动态分配,从而提高效率和利用率.就像是餐厅的假期工,当假期中餐厅客流量大的时候已有的服务员不足以完成大量工作,为了餐厅正常运行,有的老板就会选择找假期工来度过这一段高峰期,等到客流量少的时候就可以不用招假期工,这样既可以实现餐厅增加营收,也不用像正式员工一样付出高薪,从而让自己利益最大化

2. 标准库中的线程池

2.1 创建线程池

// 使用 Executors.newFixedThreadPool(10) 能创建出固定包含 10 个线程的线程池

// 返回值类型为 ExecutorService 

// 通过 ExecutorService.submit 可以注册一个任务到线程池中

// 将下列代码放入 main 函数中即可运行 

java">     ExecutorService Pool = Executors.newFixedThreadPool(10);
        Pool.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello ThreadPool");
            }
        });

// 结果如下: 

 

2.2 Executors 创建线程池的几种方式

2.2.1 newFixedThreadPool : 创建固定线程数的线程池

2.2.2 newCachedThreadPool : 创建线程数目动态增长的线程池

2.2.3 newSingleThreadExecutor : 创建只包含单个线程的线程池

2.2.4 newScheduledTheadPool : 设定延迟时间后执行命令, 或者定期执行命令, 是进阶版的 Timer

// Executors 本质上是 ThreadPoolExecutor (原生的,更常用)类的封装

2.3 ThreadPoolExecutor 提供了更多的可选参数, 可以进一步细化线程池行为的设定

// 以上图片为 Java 官方文档中截取

// TreadPoolExecutor 里面的线程个数并非是固定不变的,它会根据当前任务的情况动态发生变化(自适应)

// 要做到既能保证繁忙的时候可以高效完成任务,又能保证空闲时不会造成资源浪费

2.3.1 corePoolSize : 核心线程数 (正式员工的数量 [正式员工, 一经录用,永不辞退])

// 至少得有这些线程, 哪怕线程池一点任务都没有

2.3.2 maximumPoolSize : 最大线程数 (正式员工 + 临时工的数量 [临时工: 一段时间没活干就会被辞退])

// 最多只能这么多,不能超过这些线程, 哪怕再忙也不能比这个数目更多了

2.3.3 keepAliveTime : 临时工允许的空闲时间 (摸鱼时间)

2.3.4 unit : 它是时间单位, 可以是秒,分钟或其他值

2.3.5 workQueue : 传递任务的阻塞队列 (线程池内部有很多任务, 这些任务可以用阻塞队列来管理)

//线程池可以内置阻塞队列, 也可以手动指定一个

2.3.6 threadFactory : 工厂模式 (创建线程的工厂, 参与具体的线程创建工作, 通过不同线程工厂创建出的线程相当于对一些属性进行了不同的初始化设置)

2.3.7 RejectedExecutionHandler : 拒绝策略/拒绝方式 (如果任务量超出公司的负荷了接下来怎么处理)

2.3.7.1 AbortPolicy() : 超过负荷, 直接抛出异常

2.3.7.2 CallerRunsPolicy() : 调用者负责处理多出来的任务

2.3.7.3 DiscardOldestPolicy() : 丢弃队列中最老的任务 (喜新厌旧)

2.3.7.4 DiscardPolicy() : 丢弃新来的队伍 

3. 实现线程池

3.1 主要操作说明

// 核心操作位 submit, 将任务加入线程池中

// 使用 Worker 类描述一个工作线程. 使用 Runnable 描述一个任务

// 使用 BlockingQueue 组织所有的任务

// 每个 Worker 线程要做的事情 : 不停的从 BlockingQueue 中取任务并执行

// 指定一下线程池中的最大线程数 maxWorkerCount; 当当前线程数超过这个最大值时, 就不再新增新的线程了

3.2 代码实现

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

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

    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();

        }
    }
}

// 以上是实现线程池的核心代码

java"> public static void main(String[] args) {
        ExecutorService Pool = Executors.newFixedThreadPool(10);
        Pool.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello ThreadPool");
            }
        });
    }

 // 以上是主函数中运用线程池的代码


http://www.niftyadmin.cn/n/5865985.html

相关文章

java给钉钉邮箱发送邮件

1.开通POP和IMAP 2.引入pom <dependency><groupId>javax.mail</groupId><artifactId>mail</artifactId><version>1.4.7</version> </dependency>3.逻辑 String host "smtp.qiye.aliyun.com"; String port "…

C#中提供的多种集合类以及适用场景

在 C# 中&#xff0c;有多种集合类可供使用&#xff0c;它们分别适用于不同的场景,部分代码示例提供了LeetCode相关的代码应用。 1. 数组&#xff08;Array&#xff09; 特点 固定大小&#xff1a;在创建数组时需要指定其长度&#xff0c;之后无法动态改变。连续存储&#xf…

深度学习-6.用于计算机视觉的深度学习

Deep Learning - Lecture 6 Deep Learning for Computer Vision 简介深度学习在计算机视觉领域的发展时间线 语义分割语义分割系统的类型上采样层语义分割的 SegNet 架构软件中的SegNet 架构数据标注 目标检测与识别目标检测与识别问题两阶段和一阶段目标检测与识别两阶段检测器…

.manifest是什么文件格式

.manifest 文件是一种用于描述应用程序或组件元数据的文件&#xff0c;其格式和内容因平台和应用类型而异。在某些情况下&#xff0c;.manifest 文件采用 JSON 格式&#xff0c;例如在 Web 应用程序中&#xff0c;manifest.json 文件用于定义应用的名称、版本、图标、启动页面等…

04基于vs2022的c语言笔记——数据类型

目录 前言 4.数据类型 4-1数据类型初识 4-2数据类型之整型 4-3 sizeof的应用 4-4unsigned的应用 4-5实型/浮点型 4-6字符型 4-7转义字符 4-8字符串初识 4-9-1 输入之 整数的输入 提示&#xff1a; 本节代码部分 1.scanf的基本用法介绍 2.两个变量的输入 3.输…

Python 学习之旅:高级阶段(十六)Web 开发之路由和视图函数

在 Python 的 Web 开发领域,路由和视图函数是构建 Web 应用不可或缺的部分。它们就像是 Web 应用的 “交通枢纽” 和 “服务窗口”,路由负责引导用户请求到达正确的处理地点,而视图函数则负责处理这些请求并返回相应的响应。接下来,我们将以 Flask 框架为例,深入了解路由和…

【R语言】ggplot2绘图常用操作

目录 坐标轴以及标签的相关主题 图例调整 字体类型设置 颜色相关 ggplot2如何添加带箭头的坐标轴&#xff1f; 标题相关主题调整 修改点图中点的大小 如何使得点的大小根据变量取值的大小来改变&#xff1f; 柱状图和条形图 坐标轴以及标签的相关主题 theme( # 增大X…

强化学习笔记(一)

强化学习笔记&#xff08;一&#xff09; 回报与价值函数贝尔曼方程全期望公式自举策略马尔可夫决策过程和马尔可夫过程/马尔可夫奖励过程的区别马尔可夫决策过程中的价值函数贝尔曼期望方程备份图 参考书目&#xff1a;蘑菇书&#xff0c;链接蘑菇书 本系列笔记仅为个人学习所…