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

目录

🚩什么是线程池

🎈从池子中取效率大于新创建线程效率(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.mzph.cn/news/778587.shtml

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

相关文章

【微服务】Nacos(配置中心)

文章目录 1.AP和CP1.基本介绍2.说明 2.Nacos配置中心实例1.架构图2.在Nacos Server加入配置1.配置列表&#xff0c;加号2.加入配置3.点击发布&#xff0c;然后返回4.还可以编辑 3. 创建 Nacos 配置客户端模块获取配置中心信息1.创建子模块 e-commerce-nacos-config-client50002…

Matlab之求直角坐标系下两直线的交点坐标

目的&#xff1a;在直角坐标系下&#xff0c;求两个直线的交点坐标 一、函数的参数说明 输入参数&#xff1a; PointA&#xff1a;直线A上的点坐标&#xff1b; AngleA&#xff1a;直线A的倾斜角&#xff0c;单位度&#xff1b; PointB&#xff1a;直线B上的点坐标&#xf…

LeetCode - 股票平滑下跌阶段的数目(分组循环)

2110. 股票平滑下跌阶段的数目 当数组中的数字满足这个prices[i] 1 prices[i - 1]条件之后&#xff0c;就是平滑下降的阶段&#xff0c;也就是将数组中连续的数字进行一个分组。每次计算一个分组即可。 class Solution { public:long long getDescentPeriods(vector<int&…

如何写好一篇文档?

&#x1f304; 前言 什么是好的文档&#xff1f;在我看来&#xff0c;不减分地表达清楚作者的意图&#xff0c;即是一个不错的文档&#xff0c; 从作者角度上讲&#xff0c;能够让读者快速、清晰理解作者要表达的内容。 从读者角度上讲&#xff0c;读者能够快速、清晰地了解到…

分布式部署LNMP+WordPress

需要四台虚拟机&#xff0c;实际上&#xff0c;我们只需要操作三台 一个数据库&#xff0c;一个nginx&#xff0c;一个php&#xff0c;还需要准备一个软件包wordpress-4.7.3-zh_C 首先配置nginx的服务环境 [rootnginx ~]# vi /usr/local/nginx/conf/nginx.conf 修改文件中的loc…

蓝桥杯23年第十四届省赛真题-三国游戏|贪心,sort函数排序

题目链接&#xff1a; 1.三国游戏 - 蓝桥云课 (lanqiao.cn) 蓝桥杯2023年第十四届省赛真题-三国游戏 - C语言网 (dotcpp.com) 虽然这道题不难&#xff0c;很容易想到&#xff0c;但是这个视频的思路理得很清楚&#xff1a; [蓝桥杯]真题讲解&#xff1a;三国游戏&#xff0…

2. Java基本语法

文章目录 2. Java基本语法2.1 关键字保留字2.1.1 关键字2.1.2 保留字2.1.3 标识符2.1.4 Java中的名称命名规范 2.2 变量2.2.1 分类2.2.2 整型变量2.2.3 浮点型2.2.4 字符型 char2.2.5 Unicode编码2.2.6 UTF-82.2.7 boolean类型 2.3 基本数据类型转换2.3.1 自动类型转换2.2.2 强…

数字图像处理——直方图的均衡化

1.方法简介&#xff1a; 直方图均衡化通常用来增加许多图像的全局对比度&#xff0c;尤其是当图像的有用数据的对比度相当接近的时候。通过这种方法&#xff0c;亮度可以更好地在直方图上分布。这样就可以用于增强局部的对比度而不影响整体的对比度&#xff0c;直方图均衡化通…

电源66319D控制方法

实现自动化控制&#xff0c;电源为基础的模块&#xff0c;下面为大家讲解电源66319D的控制逻辑。 新建底层控制逻辑 在文件basis_contorl.py中写入仪器控制底层代码&#xff0c;代码如下&#xff1a; import tkinter.messagebox import pyvisaclass InstrumentControl(object…

探析Zoho Projects项目管理系统功能优势

世上万物&#xff0c;都是有难有易&#xff0c;有简单&#xff0c;也有复杂&#xff0c;项目也不例外。面对复杂的项目&#xff0c;应该如何有效管理呢&#xff1f;答案无疑是项目管理系统&#xff0c;项目管理系统有哪些强大的功能&#xff1f;Zoho Projects项目管理系统的功能…

要提升视频面试效率,需要留意以下几点

在当今远程工作的世界里&#xff0c;视频面试对于成功招聘至关重要。在这个过程中&#xff0c;过度沟通&#xff0c;要有耐心&#xff0c;提供电子产品&#xff0c;并表现出同理心。 随着世界转向远程工作&#xff0c;视频面试已经开始成为维持成功招聘策略的重要组成部分。多…

政安晨:专栏目录【TensorFlow与Keras实战演绎机器学习】

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: TensorFlow与Keras实战演绎机器学习 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 本篇是作者政安晨的专栏《TensorFlow与Keras…

碳素光线疗法与宠物健康

碳素光线与宠物健康 生息在地球上的所有动物、在自然太阳光奇妙的作用下、生长发育。太阳光的能量使它们不断进化、繁衍种族。现在、生物能够生存、全仰仗于太阳的光线。太阳光线中、包含有动物健康所需要的极为重要的波长。因此、和户外饲养的动物相比、在室内喂养的观赏动物、…

比较AI编程工具Copilot、Tabnine、Codeium和CodeWhisperer

主流的几个AI智能编程代码助手包括Github Copilot、Codeium、Tabnine、Replit Ghostwriter和Amazon CodeWhisperer。 你可能已经尝试过其中的一些&#xff0c;也可能还在不断寻找最适合自己或公司使用的编程助手。但是&#xff0c;这些产品都会使用精选代码示例来实现自我宣传…

无货源采集软件必备API--支持多平台爆款采集-淘宝-天猫-拼多多-整店采集和淘客功能

如何获取API测试权限&#xff1f; item_get 获得淘宝商品详情item_get_pro 获得淘宝商品详情高级版item_review 获得淘宝商品评论item_fee 获得淘宝商品快递费用item_password 获得淘口令真实urlitem_list_updown 批量获得淘宝商品上下架时间seller_info 获得淘宝店铺详情item…

Ubuntu通过分用户进行多版本jdk配置

前言&#xff1a;本文内容为实操记录&#xff0c;仅供参考&#xff01; linux安装jdk参考&#xff1a;http://t.csdnimg.cn/TeECj 出发点&#xff1a;最新的项目需要用jdk17来编译&#xff0c;就把服务器的jdk版本升级到了17&#xff0c;但是有一些软件例如nexus还需要jdk1.8进…

增强现实(AR)的开发工具

增强现实&#xff08;AR&#xff09;的开发工具涵盖了一系列的软件和平台&#xff0c;它们可以帮助开发者创造出能够将虚拟内容融入现实世界的应用程序。以下是一些在AR领域内广泛使用的开发工具。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎…

I.MX6ULL_Linux_系统篇(25) buildroot文件系统构建

前面我们学习了如何使用 busybox 来构建根文件系统&#xff0c;但是 busybox 构建的根文件系统不齐全&#xff0c;很多东西需要我们自行添加&#xff0c;比如 lib 库文件。在我们后面的驱动开发中很多第三方软件也需要我们自己去移植&#xff0c;这些第三方软件有很多又依赖其他…

5.6 物联网RK3399项目开发实录-Android开发之U-Boot 编译及使用(wulianjishu666)

物联网入门到项目实干案例下载&#xff1a; https://pan.baidu.com/s/1fHRxXBqRKTPvXKFOQsP80Q?pwdh5ug --------------------------------------------------------------------------------------------------------------------------------- U-Boot 使用 前言 RK U-B…

【CKA模拟题】如何用Nslookup轻松检查集群服务名的DNS解析?

题干 For this question, please set this context (In exam, diff cluster name) kubectl config use-context kubernetes-adminkubernetes Create an nginx pod named nginx-pod-cka using the nginx image, and expose it internally with a service named nginx-service-…