【JavaEE】多线程案例-线程池

在这里插入图片描述

文章目录

  • 1. 什么是线程池
  • 2. 为什么要使用线程池(线程池有什么优点)
  • 3. 如何使用Java标准库提供的线程池
    • 3.1 创建一个线程池对象
    • 3.2 什么是工厂模式
    • 3.3 为什么要使用工厂模式
    • 3.4 Executors 创建不同具有不同特性的线程池
    • 3.5 ThreadPool 类的构造方法
    • 3.6 线程池的拒绝策略
    • 3.7 调用 submit 方法添加任务
  • 4. 自己实现一个线程池

1. 什么是线程池

线程池是一种多线程处理形式,它处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池中的线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。线程池避免了在处理短时间任务时创建与销毁线程的代价,从而提高了程序的效率和性能。

2. 为什么要使用线程池(线程池有什么优点)

我们都知道,在 Java 中使用多进程效率是比较低的,因为进程的创建和销毁的开销是比较大的,这样就会导致进程的创建和销毁的速度比较慢。所以在多进程的基础上就出现了线程。线程的创建和销毁都比较轻量,多个线程共用一套资源,这就避免了多次向计算机申请资源,极大提高了代码的执行速度。但是如果一个线程多次创建和销毁的话,也会导致系统资源的频繁调用,并且创建和销毁线程的而操作是内核态的,计算机通过调用相关的 API,然后进行线程的创建和销毁,但是既然是内核态操作,那么在计算机创建和销毁线程的过程中可能不是只干了这一件事,可能还会顺便帮其他线程提供资源等,这样就降低了代码的执行速度,所以为了解决线程多次创建和销毁,并且保证线程的创建和销毁属于用户态的操作的问题,就出现了线程池这一概念。在线程池中会提前创建 n 个线程,这些线程在执行完后不会销毁,而是继续存储在线程池当中等待下一次调用,正是因为线程池的这一概念,就使得线程创建和销毁的频率降低了。

总结来说,线程池的优点有以下这些:

  1. 降低资源消耗:通过重复利用已创建的线程,降低线程创建和销毁造成的消耗。
  2. 提高响应速度:当任务到达时,任务无需等待线程创建,可以立即执行。
  3. 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性。使用线程池可以进行统一的分配,调优和监控。
  4. 避免系统过度切换:如果不使用线程池,有可能会造成系统创建大量同类线程而导致消耗完内存或者产生“过度切换”的问题。

3. 如何使用Java标准库提供的线程池

3.1 创建一个线程池对象

在Java的 java.util.concurrent 包中提供了线程池相关的方法。那么如何创建出能执行线程池相关操作的对象呢?

ExecutorService service = Executors.newScheduledThreadPool(3);

在这里插入图片描述
ExecutorService 是Java中的一个接口,它继承了Executor接口。

ExecutorService 接口在 java.util.concurrent 包中,它用于管理线程池。它提供了一种方式来管理和控制线程的生命周期。具体来说,它用于创建和管理线程池,可以执行线程,也可以关闭线程池。

Executors 则是一个工厂类,用来创建不同类型的 ThreadPoolExecutor 实例。

看到工厂类就需要提到一个常用的模式——工厂模式了,那么什么又是工厂模式呢?

3.2 什么是工厂模式

工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式,通过将对象的实例化过程封装在工厂类中,使得创建对象的方式更加灵活和可扩展。

在工厂模式中,客户端代码只需关注接口,而无需关注对象的具体创建过程。工厂模式通过提供一个统一的接口来创建不同类型的对象,这个接口定义了创建对象的标准方式。

在这里插入图片描述

3.3 为什么要使用工厂模式

工厂模式的作用是用来创建一个类的不同类型对象,既然这样的话,我们在一个类中使用多种重载的构造方法不就好了吗,为什么要多此一举再创建一个工厂类来创建一个类的不同类型的对象呢?

如果我们不使用工厂类来创建不同类型的对象,那么在创建对象的时候就需要在客户端中显式地选择合适的构造方法并提供对应的参数,这样的话类的具体创建逻辑就暴露了。而使用工厂模式的话,客户端代码只需调用工厂类的接口即可,而无需了解具体的创建逻辑。这样可以将对象的创建与使用代码分离,使得系统更加灵活,可扩展性更强。同时,使用工厂模式还可以避免在客户端代码中暴露对象的创建逻辑,提高了系统的安全性。

在这里插入图片描述
当创建线程池对象的时候,我们只需要调用 Executors 工厂类的对应静态方法,并且传递对应的参数就可以得到不同类型的 ThreadPoolExexutor 实例了,通过这个工厂模式既实现了创建一个类的不同实例的功能,又保证了系统的安全性。

3.4 Executors 创建不同具有不同特性的线程池

在知道什么是工厂模式之后,我们就利用这个工厂类来创建出需要的线程池实例,那么 Executors 工厂类又提供了哪些创建线程池对象的方法呢?它们又分别具有什么特性呢?

newFixedThreadPool() 方法
在这里插入图片描述

在这里插入图片描述

newCachedThreadPool() 方法
在这里插入图片描述
在这里插入图片描述

newScheduledThreadPool() 方法
在这里插入图片描述
在这里插入图片描述

Executors 工厂类还有很多不同的创建线程池对象的方法,这里我就不给大家一一展示出来了,大家如果感兴趣的话,可以去Java帮助文章上去查看。

3.5 ThreadPool 类的构造方法

通过查看源码,我们可以知道,Executors 工厂类创建的线程池对象都是通过传递不同的参数来实例化 ThreadPool 类的,也就是说 ThreadPool 类具有多种构成重载的构造方法,那么来看看这些不同的构造函数的参数分别代表什么吧。
在这里插入图片描述

  • corePoolSize 表示线程池中的核心线程数
  • maximumPoolSize 表示线程池中可含有的最大线程数
  • keepAliveTime 表示当线程池中的线程数量超过核心线程数(corePoolSize)时,多余的空闲线程在终止之前等待新任务的最长时间。
  • TimeUnit unit 用于指定keepAliveTime参数的时间单位。
  • workQueue 表示阻塞队列,可以根据需要设置阻塞队列的类型,如果需要优先级,则可以使用PriorityBlockingQueue;如果不需要优先级且任务的数目是恒定的,则可以使用ArrayBlockingQueue;如果任务的数目不是恒定的,则可以使用LinkedBlockingQueue
  • ThreadFactory 表示工厂类
  • RejectedExecutionHandle handle 表示拒绝策略

这里解决策略是面试中容易考的高频考点,那么这里我们就来详细的说说关于线程池的拒绝策略。

3.6 线程池的拒绝策略

当线程池中容纳的任务数量到达了最大限制之后,如果继续往里面添加任务的话,会出现什么情况呢?Java 中提供了4种拒绝策略。

  1. ThreadPoolExecutor.AbortPolicy 抛出异常
  2. ThreadPoolExecutor.CallerRunsPolicy 新添加的任务由添加任务的线程执行该任务
  3. ThreadPoolExecutor.DiscardOldestPolicy 丢弃掉最旧的未被处理的请求
  4. ThreadPoolExecutor.DiscardPolicy 丢弃掉当前新加的任务

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.7 调用 submit 方法添加任务

当创建了适当的线程池对象并且了解了其中创建的细节了之后,我们就需要调用该线程对象的相关方法来执行代码。

使用 submit 方法来添任务。

public class Demo1 {public static void main(String[] args) {ExecutorService service = Executors.newFixedThreadPool(4);service.submit(new Runnable() {@Overridepublic void run() {System.out.println("线程1");}});service.submit(new Runnable() {@Overridepublic void run() {System.out.println("线程2");}});service.submit(new Runnable() {@Overridepublic void run() {System.out.println("线程3");}});service.submit(new Runnable() {@Overridepublic void run() {System.out.println("线程3");}});}
}

在这里插入图片描述

4. 自己实现一个线程池

同样的虽然 Java 标准库提供了线程池,但是我们作为初学者如果能够自己实现一个线程池,那么对于我们理解其中的逻辑和细节很有帮助。

class MyThreadPool {//创建一个阻塞队列BlockingDeque<Runnable> queue = new LinkedBlockingDeque<>();//实现submit方法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(() -> {Runnable runnable = null;try {runnable = queue.take();} catch (InterruptedException e) {throw new RuntimeException(e);}runnable.run();});t.start();}}
}

测试

public class Demo2 {public static void main(String[] args) throws InterruptedException {MyThreadPool myThreadPool = new MyThreadPool(4);for(int i = 0; i < 4; i++) {int id = i;myThreadPool.submit(new Runnable() {@Overridepublic void run() {System.out.println("执行线程 " + id);}});}}
}

在这里插入图片描述
由于使用的是阻塞队列,所以当线程池中的任务达到数量限制的时候,如果再添加任务,会进入阻塞等待状态,这是不同于Java标准库提供的四种拒绝策略。

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

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

相关文章

C++之list成员函数应用总结(二百三十七)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

Neo4j 与 Cypher 基础

更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 简介 Neo4j 是用 Java 实现的开源 NoSQL 图数据库。从2003年开始开发&#xff0c;2007年正式发布第一版&#xff0c;其源码托管于 GitHub。 与常见的关系型数据库不同&#xff0c;Neo4j 基于图图结构来表示…

学习:原码-反码-补码

文章目录 前提知识原码详解反码补码 二进制负数的运算 前提知识 正数不需要进行原码反码补码一说&#xff0c;正数就是正数&#xff0c;我们原码反码补码是为了针对负数 &#xff08;按道理来说其实根本不存在什么码&#xff0c;只有二进制机器码&#xff0c;不过是为了方便计…

华为云云耀云服务器L实例评测|华为云上安装etcd

文章目录 华为云云耀云服务器L实例评测&#xff5c;华为云上安装etcd一、什么是etcd官方硬件建议 二、华为云主机准备三、etcd安装1. 安装预构建的二进制文件2. 从源代码构建 四、etcd服务注册与发现1. 配置etcd2. 使用systemctl 管理启动etcd服务3. 注册服务4. 发现服务 五、其…

ISE_ChipScope Pro的使用

1.ChipScope Pro Core Inserter 使用流程 在之前以及编译好的流水灯实验上进行学习 ChipScope的使用。 一、新建一个ChipScope 核 点击Next,然后在下一个框中选择 Finish&#xff0c;你就会在项目菜单中看到有XX.cdc核文件。 二、对核文件进行设置 右键“Synthesize – XST” …

最快的包管理器--pnpm创建vue项目完整步骤

1.用npm全局安装pnpm npm install -g pnpm 2.在要创建vue项目的包下进入cmd&#xff0c;输入&#xff1a; pnpm create vue 3.输入项目名字&#xff0c;选择Router,Pinia,ESLint,Prettier之后点确定 4.cd到创建好的项目 &#xff0c;安装依赖 cd .\刚创建好的项目名称\ p…

怎样快速打开github.com

1访问这个网站很慢是因为有DNS污染&#xff0c;被一些别有用心的人搞了鬼了&#xff0c; 2还有一个重要原因是不同的DNS服务器解析的速度不一样。 1 建议设置dns地址为114.114.114.114.我觉得假设一个县城如果有一个DNS服务器的话&#xff0c;这个服务器很可能不会存储…

[linux]服务器挂代理提升下载权重速度

写在前面 这里主要以huggingface下载权重为例&#xff0c;介绍如何在linux中部署代理提升下载速度 实际操作 第一步&#xff1a;服务器安装clash文件 https://github.com/Dreamacro/clash/releases#下载clash链接第二步&#xff1a;使用自己的配置文件 将config.yaml替换掉…

前端项目练习(练习-003-webpack-01)

学习webpack前&#xff0c;首先&#xff0c;创建一个web-003项目&#xff0c;内容和web-002一样。&#xff08;注意将package.json中的name改为web-003&#xff09; 想想&#xff0c;我们开发Java 的时候&#xff0c;Maven帮我们做的主要是编译&#xff0c;打包等等内容。开发前…

Spring Cloud Alibaba Gateway 简单使用

文章目录 Spring Cloud Alibaba Gateway1.Gateway简介2. 流量网关和服务网关的区别3. Spring Cloud Gateway 网关的搭建3.1 Spring Cloud Gateway 配置项的说明3.2 依赖导入3.3 配置文件 Spring Cloud Alibaba Gateway 1.Gateway简介 Spring Cloud Gateway是一个基于Spring F…

计算机竞赛 深度学习乳腺癌分类

文章目录 1 前言2 前言3 数据集3.1 良性样本3.2 病变样本 4 开发环境5 代码实现5.1 实现流程5.2 部分代码实现5.2.1 导入库5.2.2 图像加载5.2.3 标记5.2.4 分组5.2.5 构建模型训练 6 分析指标6.1 精度&#xff0c;召回率和F1度量6.2 混淆矩阵 7 结果和结论8 最后 1 前言 &…

构建自己的物料解决方案——构建物料库,实现前端设计

01: 数据拦截简化数据获取流程 /** * 响应拦截器&#xff1a; * 服务端返回数据之后&#xff0c;前端 .then 之前被调用 */ service.interceptors.response.use((response) > {const { success, message, data } response.dataif (success) {return data}// TODO&#xff…

法规标准-UN R48标准解读

UN R48是做什么的&#xff1f; UN R48全名为关于安装照明和灯光标志装置的车辆认证的统一规定&#xff0c;主要描述了对各类灯具的布置要求及性能要求&#xff1b;其中涉及自动驾驶功能的仅有6.25章节【后方碰撞预警信号】&#xff0c;因此本文仅对此章节进行解读 功能要求 …

Python中的设计模式 -- 单例

迷途小书童 读完需要 2分钟 速读仅需 1 分钟 当我们谈到单例模式时&#xff0c;可以想象一个非常特殊的餐厅&#xff0c;这个餐厅只有一个桌子&#xff0c;无论多少人来用餐&#xff0c;都只能坐在这个桌子上。这个桌子就是餐厅的单例&#xff0c;它保证了整个餐厅中只有一个桌…

Element登录+注册

Element登录注册 1.1 定义1.3 完成用户注册登录界面搭建1.3.3 下载js依赖1.3.4 创建用户登录注册组件1.3.5 配置路由 二、数据交互2.1 数据导入2.3 安装引用相关模块 2.3.1 安装相关模块2.3.2 引用相关模块2.4 axios之get请求2.5 axios之post请求 四、注册 1.1 定义 ElementUI是…

bash中执行比较的几种方法

bash 脚本中的 test 命令用于检查表达式的有效性&#xff0c;检查命令或表达式为 true 或者 false。此外&#xff0c;它还可以用于检查文件的类型和权限。 如果命令或表达式有效&#xff0c;则 test 命令返回0&#xff0c;否则返回1。 使用 test 命令 test 命令的基本语法如…

腾讯mini项目-【指标监控服务重构】2023-08-29

今日已办 Collector 指标聚合 由于没有找到 Prometheus 官方提供的可以聚合指定时间区间内的聚合函数&#xff0c;所以自己对接Prometheus的api来聚合指定容器的cpu_avg、cpu_99th、mem_avg 实现成功后对接小组成员测试完提供的时间序列和相关容器&#xff0c;将数据记录在表格…

Qt/C++音视频开发56-udp推流和拉流/组播和单播推流

一、前言 之前已经实现了rtsp/rtmp推流&#xff0c;rtsp/rtmp/hls/flv/ws-flv/webrtc等拉流&#xff0c;这种一般都需要依赖一个独立的流媒体服务程序&#xff0c;有没有一种更便捷的方式不需要这种依赖&#xff0c;然后又能实现推拉流呢&#xff0c;当然有的那就是udpp推流&a…

前端项目练习(练习-004-webpack-02)

学习前&#xff0c;首先&#xff0c;创建一个web-004项目&#xff0c;内容和web-003一样。&#xff08;注意将package.json中的name改为web-004&#xff09; 前面的例子&#xff0c;成功将js文件打包到了dist中&#xff0c;但是我们有三个文件&#xff0c;css&#xff0c;js和h…

利用C++开发一个迷你的英文单词录入和测试小程序-增强功能

小玩具基本完成之后&#xff0c;在日常工作中&#xff0c;记录一些单词&#xff0c;然后定时再复习下&#xff0c;还真的有那么一点点用&#xff08;毕竟自己做的小玩具&#xff09;。 在使用过程中&#xff0c;遇到不认识的单词&#xff0c;总去翻译软件翻译&#xff0c;然后…