【JavaEE初阶系列】——多线程案例四——线程池

news/2024/6/16 11:10:28/文章来源:https://blog.csdn.net/m0_74438843/article/details/137093817

目录

🚩什么是线程池

🎈从池子中取效率大于新创建线程效率(why)

🚩标准库中的线程池

🎈为什么调用方法而不是直接创建对象 

🎈工厂类里的方法

📝newCachedThreadPool()

📝newFixedThreadPool() 

🚩实现线程池

🚩ThreadPoolExecutor

🎈ThreadPoolExecutor的构造器参数

🎈ThreadPoolExecutor的新任务拒绝策略


🚩什么是线程池

首先我们想象一个场景就是比较渣的场景,我是一个男生,很帅并且有才华,追问的人呢,很多,排成了一个长队。然后我就挑了一个既好看又有钱又有才华的女生进行交往,交往一段时候后,我腻歪了,想换个女朋友,此时我要做俩个事情:1>想办法分手,2>再找一个小哥哥,培养感情。此时进行这俩个操作的时候,效率是很低的,有没有办法优化呢?

优化:我和这个女生A再交往的过程中,同时再和另一个女生B搞暧昧(培养感情),当我想和女生A分手的时候,就只要分手了后我们就可以和女生B直接在一起了。(此时我和女生B感情是有一定的基础了)。此时女生B就是我们所说的备胎。

进一步优化:我需要更高的效率的话,更换女朋友,就可以再和女生A在一起的时候,同时和女生B,C,D交往联络感情,此时女生B,C,D都是我的备胎,此时备胎就构成了——备胎池。

所以和线程池有同样的方式,线程池顾名思义就是存放线程的池子,等需要了就直接调用了,省去了启动和销毁的损耗了。

线程池最大的好处就是减少每次启动、销毁线程的损耗


从上面线程池我们知道,等需要了就直接从线程池中取,但是为什么从池子取得效率比新创建线程得效率更高呢?

🎈从池子中取效率大于新创建线程效率(why)

  • 池子中取,这个动作,是纯粹用户态得操作
  • 创建新的线程,这个动作,是需要用户态+内核态相互配合完成的操作。

如果一段程序,是在系统内核中执行,此时就称为"内核态",如果不是,则称为"用户态".

操作系统,是由 内核+配套 的应用程序构成的,内核则是 系统最核心的部分,创建线程操作,就需要调用系统api,进入内核中,按照内核态的方式来完成一系列操作。

场景:

滑稽老哥去银行存钱,但是需要身份证复印件,但是滑稽老哥没有,所以滑稽老哥就有俩个选择。

  • A:银行柜员说:你可以给身份证给我,我去帮你打印 
  • B:银行柜员又说: 大厅的角落,有一个自助复印机,你可以自行去复印。

A这个过程就是涉及到了内核态操作了,所谓内核态就是柜员要进行的操作,此时你交给柜员后,柜员会在给你复印件之前会做哪些工作(因为操作系统内核是给所有的进程提供服务的,当你要创建线程的时候,人家内核会帮你做,但是做的过程,难免会做一些其他的事情)——不可控

B这个过程就是纯粹用户态的操作,所谓用户态就是用户自己要进行的操作,滑稽老哥就可以立即去复印,复印完了之后就立即回来,整个过程中,没有任何拖泥带水的。——可控


🚩标准库中的线程池

  • 使用 Executors.newFixedThreadPool(10) 能创建出固定包含 10 个线程的线程池.
  • 返回值类型为 ExecutorService

线程池对象不是直接创建出来的,而是通过一个专门的方法,返回一个线程池对象。

ExecutorService service= Executors.newCachedThreadPool();
  • 通过 ExecutorService.submit 可以注册一个任务到线程池中

Executors.newCachedThreadPool();其实是个工厂模式(设计模式),也就是Executors是个工厂类,需要创建线程,但是为什么调用方法呢?而不是直接创建线程池对象呢?

🎈为什么调用方法而不是直接创建对象 

创建对象的同时,new关键字就会触发类的构造方法,但是构造方法,存在一定的局限性。

考虑有个类,我们期待用笛卡尔坐标系,来构造对象,又或者用极坐标构造对象,写在一起的时候,这俩个方法是无法重载的(也就是说在一个类中我们要实现不同方式的初始化),就会编译失败。其实很多时候,构造一个对象,希望有多种构造方式,多种方式,就需要使用多个版本的构造方法来分别实现,但是构造方法要求方法的名字必须是类名,不同的构造方法,就只能通过 重载 的方式区分了。(重载是方法名相同,参数个数类型不同),使用工厂模式/设计模式,就能解决这个问题,使用普通的方法,代替构造方法来完成初始化工作,普通方法就可以通过方法名的不同来进行区分了,不必因为重载的规则而限制了。

通过这种,我们通过一个工厂类Executors调用方法创建不同类型的初始化工作。Executors是工厂类,那么调用的方法是工厂方法,然后加工好之后,返回的是整个加工好的线程,而ExecutorService就是线程池,是由工厂类调用工厂方法创建好的。

实践中,一般单独搞一个类,给这个类搞一些静态方法,由这样静态方法负责构造出对象

class PointFactory{public static Point makePointByXY(int x,int y){};public static Point makePointByRA(int R,int A){};

等到需要调用哪个的时候,我们就可以通过类来调用方法。


🎈工厂类里的方法

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

📝newCachedThreadPool()

ExecutorService service= Executors.newCachedThreadPool();

newCachedThreadPool()方法中cached缓存,用过之后不着急释放,先留着以备下次使用(此时构造出的线程池对象,有一个基本特点,线程数目是能够动态适应的)随着往线程池中添加任务,这个线程池中的线程会根据需要自动被创建出来,创建出来之后也不会着急销毁,会在池子里保留一定的时间,以备随时再使用。


📝newFixedThreadPool() 

ExecutorService service1=Executors.newFixedThreadPool(4);

固定的,指定创建几个线程。具体需要创建几个线程,正确做法就是使用实验的方式,对程序进行性能测试,测试过程中尝试修改不同的线程池的线程数目,看哪种情况下,最符合你的要求。

 还有些工厂方法了解即可。


🚩实现线程池

  • 核心操作为 submit, 将任务加入线程池中
  • 使用 MyThread 类描述一个工作线程. 使用 Runnable 描述一个任务.
  • 使用一个 BlockingQueue 组织所有的任务
  • 每个 t 线程要做的事情: 不停的从 BlockingQueue 中取任务并执行.
package ThreadPool;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;class MyThread{BlockingQueue<Runnable> queue=new ArrayBlockingQueue<Runnable>(1000);public void submit(Runnable runnable){queue.offer(runnable);}public void takeTask(int n){for (int i = 0; i < n; i++) {Thread t=new Thread(()->{try {Runnable runnable=queue.take();runnable.run();} catch (InterruptedException e) {throw new RuntimeException(e);}});t.start();}}
}
public class ThreadPool_test {public static void main(String[] args) {MyThread myThread=new MyThread();for (int i = 0; i <100; i++) {//一百个任务myThread.submit(new Runnable() {@Overridepublic void run() {System.out.println("我爱zyf");}});}myThread.takeTask(10);//10个线程执行100个任务}
}

 打印了十个,10个线程执行了10个任务,因为里面没有用while(true)循环,一个线程执行完任务之后就结束了。但是这些线程是可能同时执行各自的任务,但是一个线程肯定是执行一个任务。


🚩ThreadPoolExecutor

在阿里巴巴手册中有一条建议:

【强制】线程池不允许使用 Executors 去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

如果经常基于Executors提供的工厂方法创建线程池,很容易忽略线程池内部的实现。特别是拒绝策略,因使用Executors创建线程池时不会传入这个参数,直接采用默认值,所以常常被忽略

ThreadPoolExecutor可以实现线程池的创建。ThreadPoolExecutor相关类图如下

从类图可以看出,ThreadPoolExecutor最终实现了Executor接口,是线程池创建的真正实现者。


ThreadPoolExecutor核心方法有俩个,一个是构造方法,一个是注册任务(添加方法).

🎈ThreadPoolExecutor的构造器参数

 public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
  • 参数一
  • 指定线程池的线程数量(核心线程): corePoolSize,不能小于0;
  • 参数二
  • 指定线程池可支持的最大线程数: maximumPoolSize,最大数量 >= 核心线程数量;
  • 参数三
  • 指定临时线程的最大存活时间: keepAliveTime,不能小于0;(则表示实习生可以摸鱼的时间,并不代表一摸鱼就被开除了)
  • 参数四
  • 指定存活时间的单位(秒、分、时、天): unit,时间单位;
  • 参数五
  • 指定任务队列: workQueue,不能为null;
  • 参数六
  • 指定用哪个线程工厂创建线程: threadFactory,不能为null;
  • 参数七
  • 指定线程忙,任务满的时候,新任务来了怎么办: handler,不能为null;
  • 临时线程触发机制
  • 新任务提交时发现核心线程都被占用,任务队列也满了,但还可以创建临时线程,此时才会创建临时线程。
  • 何时拒绝任务
  • 核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始任务拒绝。

“核心线程”如何理解呢?

如果把一个线程池,理解成一个公司,此时,公司里有俩类员工,一批是正式员工(有编制的),另一批是实习生(无编制的),正式员工的数目就是核心线程数,最大线程数就是正式员工+实习生。这个线程池里线程的数目是可以动态变化的,变化的范围就是[corePoolSize,maxmumPoolSize],正式员工可以摸鱼,不会因为摸鱼,被公司开除,但是实习生不允许摸鱼,如果这段时间任务多了,就可以多搞几个实习生,来干活,如果过段时间任务少了,并且少的状态持续了一段时间,空闲的实习生就被裁掉了。(这样做,既可以满足效率的要求,又可以避免过多的系统开销)

BlockingQueue<Runnable> workQueue 阻塞队列,用来存放线程池中的任务的,可以根据需要灵活设置这里的队列是啥,需要优先级,就可以设置PriorityBlockingQueue,如果不需要优先级,并且任务数目是相对恒定的,可以使用ArrayBlockingQueue,如果不需要优先级,并且任务数目变动较大,就用LinkedBlockingQueue。

ThreadFactory   工厂模式的体现,此处使用ThreadFactory作为工厂类,由这个类负责创建线程,使用工厂类创建线程,主要是为了再创建过程中,对线程的属性做出一些设置。(如果手动创建线程,就得手动设置在这些属性,就比较麻烦,使用工厂方法封装一下)

RejectedExecutionHandler  线程池的拒绝策略,一个线程池,能容纳的任务数量,是有上限的,当持续往线程池里添加任务的时候,一旦已经达到上限了,继续添加,会出现什么效果呢?(不同的拒绝策略,就有不同的效果)

🎈ThreadPoolExecutor的新任务拒绝策略

就比如一个学校老师,一个星期得上8节课,学校领导找到我,想让我去参加校园活动。

  • 1.听到这个要求的时候,老师心态崩了,心情很烦躁——这属于(.AbortPolicy直接抛出异常)
  • 2.老师直接和领导说,她这边有好多课去不了,让领导一个人去参加校园活动(.CallerRunsPolicy拒绝新任务,由新增任务的线程去执行)
  • 3.老师给这一周8节课中一节课给割了,然后和领导一起去参加校园活动(.DiscardOldestPolicy丢弃任务队列中最老的任务,执行新任务去)
  • 4.老师拒绝了校领导,继续去上课,然后校领导也不去了,这个校园活动都不去参加了。(DisCardPolicy丢弃新加的任务,新加任务的线程也丢弃了)

在面试中,拒绝策略和线程数目是面试的重点。


保持现状。

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

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

相关文章

使用Leaflet.rotatedMaker进行航班飞行航向模拟的实践

目录 前言 一、Leaflet的不足 1、方向插件 2、方向控制脚本说明 二、实时航向可视化实现 1、创建主体框架 2、飞机展示 3、位置和方位模拟 三、成果及分析 1、成果展示 2、方向绑定解读 总结 前言 众所周知&#xff0c;物体在空间中的运动&#xff08;比如飞行、跑步…

【Linux】ubuntu/centos8安装zsh终端

本文首发于 ❄️慕雪的寒舍 根据这篇知乎文章进行 https://zhuanlan.zhihu.com/p/514636147 1.安装zsh 先安装zsh并设置为默认的终端 # ubuntu sudo apt install zsh # centos sudo yum install zsh util-linux-user # 通用 chsh -s /bin/zsh如果centos下找不到chsh命令&am…

深入解析实时数仓Doris:Rollup上卷表与查询

码到三十五 &#xff1a; 个人主页 心中有诗画&#xff0c;指尖舞代码&#xff0c;目光览世界&#xff0c;步履越千山&#xff0c;人间尽值得 ! 目录 一、基本概念二、Aggregate 和 Unique 模型中的 ROLLUP三、Duplicate 模型中的 ROLLUP四、ROLLUP 调整前缀索引五、ROLLUP使…

改Jenkins版本号

旧服务器迁移到新&#xff0c;打包版本号更新 Jenkins.instance.getItemByFullName("双机热备").updateNextBuildNumber(65)

154 Linux C++ 通讯架构实战9 ,信号功能添加,信号使用sa_sigaction 回调,子进程添加,文件IO详谈,守护进程添加

初始化信号 使用neg_init_signals(); 在nginx.cxx中的位置如下 //(3)一些必须事先准备好的资源&#xff0c;先初始化ngx_log_init(); //日志初始化(创建/打开日志文件)&#xff0c;这个需要配置项&#xff0c;所以必须放配置文件载入的后边&#xff1b;//(4)一些初…

【SpringBoot整合系列】SpirngBoot整合EasyExcel

目录 背景需求发展 EasyExcel官网介绍优势常用注解 SpringBoot整合EaxyExcel1.引入依赖2.实体类定义实体类代码示例注解解释 3.自定义转换器转换器代码示例涉及的枚举类型 4.Excel工具类5.简单导出接口SQL 6.简单导入接口SQL 7.复杂的导出&#xff08;合并行、合并列&#xff0…

C++刷题篇——04找等值元素

一、题目 二、解题思路 1、分割后放进二维数组 2、使用map&#xff0c;key为数值&#xff0c;value为其坐标 3、遍历二维数组元素&#xff0c;再在map中找该元素对应的value值&#xff08;二维数组形式&#xff09;&#xff0c;倘若value.size为1&#xff0c;那直接返回-1&…

每天五分钟计算机视觉:使用神经网络完成人脸的特征点检测

本文重点 我们上一节课程中学习了如何利用神经网络对图片中的对象进行定位,也就是通过输出四个参数值bx、by、bℎ和bw给出图片中对象的边界框。 本节课程我们学习特征点的检测,神经网络可以通过输出图片中对象的特征点的(x,y)坐标来实现对目标特征的识别,我们看几个例子。…

uniapp-打包IOS的APP流程

打包前所需配置 在manifest文件内配置 1. APP图标 2. 启动界面 有三种启动界面配置 第一种是 HBuilderX 官方给的通用启动界面&#xff0c;页面单一&#xff0c;屏幕中间就一个圆框图标 第二种是自定义的启动图&#xff0c;无法通过AppStore的审核 第三种是自定义storyboard启动…

蚁剑流量分析

蚁剑流量分析 在靶机上面上传一个一句话木马&#xff0c;并使用蚁剑连接&#xff0c;进行抓包, 一句话木马内容 <?php eval($_POST[1]); defalut编码器 在使用蚁剑连接的时候使用default编码器 连接之后进行的操作行为是查看当前目录(/var/www/html)下的文件&#xff0…

MySQL-主从复制与读写分离

一、MySQL主从复制&#xff1a; 1、主从复制的作用&#xff1a; 主从复制&#xff1a;主设备通过二进制日志传输到从设备&#xff0c;从设备通过二进制日志和主同步数据。 作用&#xff1a;负载均衡读操作&#xff0c;备份(实时备份&#xff0c;不能替换手动的备份)&#xf…

(QT5142、OpenCV452、SeetaFace2 )facetest

服务端 .pro QT core gui network sql#window平台opencv&#xff0c;seetaface环境 win32{ LIBS C:\opencv452\x64\mingw\lib\libopencv* LIBS C:\SeetaFace\lib\libSeeta* INCLUDEPATH C:\opencv452\include INCLUDEPATH C:\opencv452\include\opencv2 INCLUDEPATH …

技术分享 | 如何写好测试用例?

对于软件测试工程师来说&#xff0c;设计测试用例和提交缺陷报告是最基本的职业技能。是非常重要的部分。一个好的测试用例能够指示测试人员如何对软件进行测试。在这篇文章中&#xff0c;我们将介绍测试用例设计常用的几种方法&#xff0c;以及如何编写高效的测试用例。 ## 一…

Nginx配置导致请求成环的问题

前期介绍 在一台主机上部署LAMP&#xff0c;之后使用Nginx实现反向代理&#xff0c;并且实现动静分离。 apache的访问端口为80&#xff0c;Nginx&#xff0c;访问端口为8001端口。 首先可以实现反向代理。 [rootlnmp-247 ~]# cat /usr/local/nginx/conf/nginx.conf worker_…

FPGA之组合逻辑与时序逻辑

数字逻辑电路根据逻辑功能的不同&#xff0c;可以分成两大类&#xff1a;组合逻辑电路和时序逻辑电路&#xff0c;这两种电路结构是FPGA编程常用到的&#xff0c;掌握这两种电路结构是学习FPGA的基本要求。 1.组合逻辑电路 组合逻辑电路概念&#xff1a;任意时刻的输出仅仅取决…

k8s笔记28--快速在ubuntu上基于二进制和源码安装containerd

k8s笔记28--快速在ubuntu上基于二进制和源码安装containerd 介绍containerd 安装方法二进制文件安装源码构建安装 注意事项说明 介绍 Containerd是一个工业标准的容器运行时&#xff0c;它强调简单、健壮和可移植性。它可作为Linux和Windows的守护进程&#xff0c;能管理主机系…

十四.PyEcharts基础学习

目录 1-PyEcharts介绍 优点&#xff1a; 安装: 官方文档&#xff1a; 2-PyEcharts快速入门 2.1 第一个图表绘制 2.2 链式调用 2.3 opeions配置项 2.4 渲染图片文件 2.5 使用主题 3-PyEcharts配置项 3.1 初始化配置项InitOpts InitOpts 3.2 全局配置项set_global_o…

Python爬虫逆向:揭秘网站反爬虫技术与应对策略

目录 前言 一、网站反爬虫技术概述 1. User-Agent检测 2. IP限制 3. 图像验证码 4. 动态渲染 5. 反爬虫算法 二、Python爬虫逆向的基本原理 三、应对网站反爬虫技术的代码示例 1. User-Agent检测&#xff1a; 2. IP代理&#xff1a; 3. 验证码识别&#xff1a; 4.…

如何将本地仓库放到远程仓库中

在我们仓库创建好之后&#xff0c;我们复制好ssh 接着我们需要使用git remote add<shortname><url>这个命令 shortname就是我们远程仓库的别名 接着使用git remote -v这个命令查看一下目前远程仓库的别名和地址 原本还有一个指令git branch -M main 指定分支的名…

vitess执行计划缓存 测试

打开执行计划器缓存&#xff1a; sysbench /usr/local/share/sysbench/oltp_write_only.lua --mysql-host127.0.0.1 --mysql-port15306 --mysql-userroot --mysql-password --mysql-dbcustomer --report-interval10 100s sysbench /usr/local/share/sysbench/oltp_read_only.l…