「JavaEE」多线程案例分析3:线程池

news2024/12/26 21:31:16

🎇个人主页:Ice_Sugar_7
🎇所属专栏:JavaEE
🎇欢迎点赞收藏加关注哦!

线程池

  • 🍉简介
  • 🍉标准库中的线程池
  • 🍉Executors
  • 🍉实现一个简单的线程池

🍉简介

“池”是一个非常重要的概念,我们之前学的字符串常量池就是一种常量池

在这里插入图片描述

除了常量池,还有数据库连接池、线程池、进程池、内存池……
池有两个作用:

  1. 提前把要用的对象准备好
  2. 对象用完后也不要立即释放,而是先留着,以备下次使用

我们主要来分析线程池
它是存放线程的池,会把要使用的线程提前创建好,用完之后也不会立即释放,而是放回线程池里。这样就可以节省创建及销毁线程的开销

那为什么从线程池里取线程就比向系统申请更高效呢?
因为从池里取,这部分是通过代码实现的,代码是我们自己写的,是纯用户态的(可控的);而向系统申请创建线程是需要内核来完成的,这个过程是不太可控的
就是说你不知道系统在创建线程之前会不会去做别的事儿,它如果有搞别的,那肯定会耽误时间


🍉标准库中的线程池

标准库提供了 ThreadPoolExecutor 这个类,它的构造方法有很多参数

在这里插入图片描述

我们分别来解释一下每个参数的含义

  1. corePoolSize —— 核心线程数
    标准库提供的线程池,它所持有的线程数并非是一成不变的,而是会根据当前任务量,自适应调节线程个数。即如果任务非常多,那就会多搞几个线程;反之则会缩减线程数
    核心线程数就是一个线程池里最少有多少个线程

  2. maximumPoolSize —— 最大线程数
    顾名思义,就是最多能有多少个线程

  3. keepAliveTime —— 保持存活时间
    就是某个线程,它的空闲时间如果超过这个时间阈值,那就会被销毁掉(或者说空闲状态下能存活多久)

  4. unit —— 保持存活时间的时间单位(h、min、s、ms……)

  5. workQueue —— 一个阻塞队列
    和定时器类似,线程池中也可以持有多个任务,所以用一个阻塞队列来存放任务,如果任务有优先级的话,可以用 PriorityBlockingQueue。Runnable 是用来描述任务的主体

  6. threadFactory —— 线程工厂
    首先要解释一下什么是工厂模式
    工厂模式是一种常见的设计模式,通过专门的“工厂类”/“工厂对象”来创建指定的对象
    它本质上是给 Java 语法填坑的。这里的“坑”指的是,方法重载没办法在返回值相同的前提下,重载参数个数相同、类型一样的方法
    而有时候要重载的方法,它们参数列表中的参数个数和类型虽然一样,但是意思不一样

举个例子,现在有一个类 Point,要用它来表示平面直角坐标系中的点,在数学中有两种表达形式:常规的坐标和极坐标

public class Point {
    double x,y;
    double r,a;
    Point(double x,double y) { //用坐标的形式表示
        this.x = x;
        this.y = y;
    }

    Point(double r,double a) { //用极坐标的形式表示
        this.r = r;
        this.a = a;
    }
}

这样写编译器会报错
我们可以把原先两个方法改成静态方法,直接在方法内部创建一个对象并初始化,然后再返回这个对象
这种把创建对象和初始化封装起来,在静态方法内部完成这些操作的过程,想得到对象,就调用这个方法,它就会“加工”出一个对象,这就是工厂模式。代码如下:

public class Point {
    public double x,y;
    public double r,a;
    public static Point pointByXY(double x,double y) { //用坐标的形式表示
        Point p = new Point();
        p.x = x;
        p.y = y;
        return p;
    }

    public static Point pointByRA(double r,double a) { //用极坐标的形式表示
        Point p = new Point();
        p.r = r;
        p.a = a;
        return p;
    }

    public static void main(String[] args) {
        Point p1 = Point.pointByXY(1,2);
        Point p2 = Point.pointByRA(3,4);
    }
}

pointByXY 和 pointByRA 就称为工厂方法
这两个工厂方法是放在 Point 里面,所以 Point 就叫作工厂类

  1. handler —— 拒绝策略
    这个参数是最重要的一个参数
    线程池中的阻塞队列能够容纳的元素是有上限的,如果往队列中添加任务时,任务队列已经满了,那线程池会怎么做呢?
    这就涉及到拒绝策略了

有四种策略
AbortPolicy直接抛出异常。此时原先任务和新任务都不执行
CallerRunsPolicy新的任务由添加任务的线程去执行,就是“谁揽的活谁去干”。此时新任务就不由线程池执行了
DiscardOldestPolicy丢弃最老的任务
DiscardPolicy丢弃最新的任务


🍉Executors

由于 ThreadPoolExecutor 参数比较多,用起来比较复杂,所以标准库还提供了另一个版本 —— 工厂类 Executors,它是由 ThreadPoolExecutor 封装后得到的,通过 Executors 创建出来的线程池对象,它内部已经把 ThreadPoolExecutor 创建好了并设置了参数

可以看到已经提供了多种现成的线程池
在这里插入图片描述

Q:Executors 和 ThreadPoolExecutor 分别在什么时候使用?
A:如果没什么要求,只是简单使用一下,那就用 Executors;而如果想要高度定制化(diy),那就使用 ThreadPoolExecutor,参数都由自己设置,掌控权在我们手上,这样可以避免一些不可控因素


🍉实现一个简单的线程池

方便起见,我们写一个线程数目固定的线程池

构造方法指定创建多少个线程,并把这些线程创建好。然后要有一个阻塞队列,持有要执行的任务。还要提供一个方法 —— submit,它用来添加新任务

public class MyThreadPool {
    private List<Thread> list = new ArrayList<>();
    private BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1000);

    public MyThreadPool(int n) {
        for(int i = 0;i < n;i++) {
            Thread t = new Thread(()-> {
                try {
                    while (true) {
                        Runnable runnable = queue.take(); //取出任务并执行
                        runnable.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            t.start();
            list.add(t);
        }
    }

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

测试一下:

public class TestDemo5 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool executor = new MyThreadPool(4); //4个线程
        for (int i = 0; i < 1000; i++) {
            int n = i;
            executor.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("执行任务:" + n + " 当前线程为:" + Thread.currentThread());
                }
            });
        }
    }
}

因为这里每次循环都会改变 i 的值,所以不能直接打印语句中不能写 i,而要重新定义一个变量 n,这个 n 在这里就是“事实 final”,可以放到打印语句中
补充:run 是回调函数,它访问外部作用域的变量就是变量捕获,而变量捕获要求变量是常量 or “事实 final”

结果如下:

在这里插入图片描述

因为线程是随机调度的,所以任务序号是无序的

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

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

相关文章

初始Django

初始Django 一、Django的历史 ​ Django 是从真实世界的应用中成长起来的&#xff0c;它是由堪萨斯&#xff08;Kansas&#xff09;州 Lawrence 城中的一个网络开发小组编写的。它诞生于 2003 年秋天&#xff0c;那时 Lawrence Journal-World 报纸的程序员 Adrian Holovaty 和…

识别AI论文生成内容,降低论文高AI率

AI写作工具能帮我们在短时间内高效生成一篇毕业论文、开通报告、文献综述、任务书、调研报告、期刊论文、课程论文等等&#xff0c;导致许多人开始使用AI写作工具作为撰写学术论文的辅助手段。而学术界为了杜绝此行为&#xff0c;开始使用AIGC检测系统来判断文章是由AI生成还是…

机器学习中的聚类

目录 认识聚类算法 聚类算法API的使用 聚类算法实现流程 聚类算法模型评估 认识聚类算法 聚类算法是一种无监督的机器学习算法。 它将一组数据分成若干个不同的群组&#xff0c;使得每个群组内部的数据点相似度高&#xff0c;而不同群组之间的数据点相似度低。常用的相似…

【3dmax笔记】028:倒角的使用方法

一、倒角描述 在3dmax中创建倒角效果可以通过多种方法实现,以下是几种常见的方法: 使用倒角修改器。首先创建一个图形(如矩形和圆),然后对齐它们,将它们转化为可编辑样条线,并附加在一起,选择要倒角的边缘,然后使用倒角修改器来调整高度、轮廓等参数。使用倒角剖面修…

Davinci工程vBaseEnv模块讲解

配合的模块 要正常使用vBaseEnv模块&#xff0c;要同时使能EcuC、OS、vBRS和vLinkGen模块。 OS是操作系统&#xff0c;除了FBL以外&#xff0c;其他都需要用到OS。 vBaseEnv是基础环境 vBRS是基本运行系统 vLinkGen脚本链接生成器 EcuC是ECU配置 EcuC配置 需要配合vBaseEnv模…

程序员的神奇应用:从代码创造到问题解决的魔法世界之持续集成/持续部署

文章目录 持续集成/持续部署 在软件开发的海洋中&#xff0c;程序员的实用神器如同航海中的指南针&#xff0c;帮助他们导航、加速开发、优化代码质量&#xff0c;并最终抵达成功的彼岸。这些工具覆盖了从代码编写、版本控制到测试和部署的各个环节。 在当今数字化的世界里&…

数字水印 | Arnold 变换的 Python 代码实现

&#x1f96d; 参考博客&#xff1a; Arnold 阿诺德置乱&#xff08;猫脸变换&#xff09;图像盲水印注入预处理&#xff08;Python&#xff09; 1 回顾&#xff1a;Arnold 公式 A r n o l d \mathsf{Arnold} Arnold 变换公式如下&#xff1a; [ x n 1 y n 1 ] [ 1 b a a b…

搜索引擎的设计与实现(二)

目录 3 搜索引擎的基本原理 3.1搜索引擎的基本组成及其功能 l.搜索器 (Crawler) 2.索引器(Indexer) 3.检索器(Searcher) 4.用户接口(UserInterface) 3.2搜索引擎的详细工作流程 4 系统分析与设计 4.1系统分析 4.2系统概要设计 4.2系统实现目标 前面内容请移步 搜索引…

力扣HOT100 - 70. 爬楼梯

解题思路&#xff1a; 动态规划 注意 if 判断和 for 循环 class Solution {public int climbStairs(int n) {if (n < 2) return n;int[] dp new int[n 1];dp[1] 1;dp[2] 2;for (int i 3; i < n; i) {dp[i] dp[i - 1] dp[i - 2];}return dp[n];} }

Co-Driver:基于 VLM 的自动驾驶助手,具有类人行为并能理解复杂的道路场景

24年5月来自俄罗斯莫斯科研究机构的论文“Co-driver: VLM-based Autonomous Driving Assistant with Human-like Behavior and Understanding for Complex Road Scenes”。 关于基于大语言模型的自动驾驶解决方案的最新研究&#xff0c;显示了规划和控制领域的前景。 然而&…

智能组网实施步骤?

随着信息技术的快速发展&#xff0c;智能组网正在逐渐成为各个行业的关注焦点。智能组网通过将各种设备、终端和系统连接起来&#xff0c;实现信息的传输和共享&#xff0c;从而提升工作效率和运营效益。本文将介绍智能组网的实施步骤&#xff0c;以及一家名为【天联】的智能组…

台式电脑屏幕亮度怎么调节?让你的眼睛更舒适!

在日常使用台式电脑时&#xff0c;调节屏幕亮度是一项常见的需求。不同的环境和个人偏好可能需要不同的亮度设置。因此&#xff0c;了解台式电脑屏幕亮度怎么调节是非常重要的。本文将介绍三种常见的方法&#xff0c;帮助您轻松调节台式电脑屏幕亮度&#xff0c;以满足您的需求…

【C++初阶】第十一站:list的介绍及使用

目录 list的介绍及使用 1.list的含义 2.list的介绍 3.list的使用 1.list的构造 2.list iterator的使用 3.list capacity 4.list element access 5 list modifiers 尾插尾删 和 头插头删 insert 和 erase resize swap clear 6.list sort and reverse 7.list copy vector copy li…

数字化转型:超越信息化,构建数字化营销体系!

在数字化的浪潮中&#xff0c;企业仅仅做好信息化数字能力基础已远远不够&#xff01;那么&#xff0c;还需要什么呢&#xff1f;没错&#xff0c;就是在数字化经营思维的指导下&#xff0c;建立起数字化营销体系和执行方案&#xff01;这是企业在数字时代中立于不败之地的关键…

在cmd中,如何使用cd进入指定文件目录

在cmd中&#xff0c;如何使用cd进入指定文件目录 1.要进入的磁盘与当前磁盘一致 例如: cd C:\Program Files (x86)\Google\Chrome\Application 2.进入到其他磁盘&#xff0c; 例如 cd /d D:\JAVA\codes\01\1.4 或者下面的方式&#xff08;直接输入磁盘F&#xff1a;和文件名…

11.php-fpm模板(监控页面取值)

php-fpm模板(监控页面取值) 开启监控页面配置 #修改php配置文件 vim /etc/php-fpm.d/www.conf pm.status_path /php_status#修改nginx配置文件,添加到nginx配置文件中 vim /etc/nginx/conf.d/default.conflocation /php_status {root html;fastcgi_pass 127.0.…

使用单片机的IO引脚直接驱动段码屏

使用单片机的IO引脚直接驱动段码屏,目的是为了降低成本。这种古老的应用,在低功耗产品中比较多见。 如:水表&#xff0c;燃气表等需要电池供电的产品。 下面纯属个人理解&#xff0c;未经测试。 1/3Duty表示LCD共有3个COM引脚,分别占显示周期的1/3 1/2BIAS表示电压0和VCC 1、…

想半天憋不出几个字?试试AI扩写

大家在写文章时是否也经常这样&#xff1f;想了半天&#xff0c;结果只能写出几个字&#xff0c;但是要求往往又是几百多个字&#xff0c;那么有没有啥工具可以帮我们在原文的基础上扩写一下文章字数&#xff0c;让我们达到字数要求呢&#xff1f; 下面给大家介绍一下如何扩写文…

Rpcx (二):传输

一、Transport 传输 rpcx 可以通过 TCP、HTTP、UnixDomain、QUIC和KCP通信。你也可以使用http客户端通过网关或者http调用来访问rpcx服务。 TCP 这是最常用的通信方式。高性能易上手。可以使用TLS加密TCP流量。 Example: 101basic 服务端使用 tcp 做为网络名并且在注册中心…

什么是Data Lakehouse Architecture(DLA)?企业为何要创建DLA

公司捕获和存储的数据比以往任何时候都多&#xff0c;因为它们依赖数据来做出关键的业务决策、改进服务或产品&#xff0c;或为最终用户&#xff08;客户&#xff09;提供更好的服务。了解各种大数据存储技术对于为商业智能&#xff08;BI&#xff09;、数据分析和机器学习&…