(JavaEE)(多线程案例)线程池 (简单介绍了工厂模式)(含经典面试题ThreadPoolExector构造方法)
线程诞生的意义,是因为进程的创建/销毁,太重了(比较慢),虽然和进程比,线程更快了,但是如果进一步提高线程创建销毁的频率,线程的开销就不能忽视了。
这时候我们就要找一些其他的办法了。
有两种典型的办法可以进一步提高这里的效率:
1: 协程 (轻量级线程,相比于线程,把系统调度的过程给省略了,变成由程序员手工调度)
(当下,一种比较流行的并发编程的手段,但是在Java圈子里,协程还不够流行,GO和Python用的比较多)
2:线程池(Java用的)
接下来我们就来介绍一些线程池
线程池
优化频繁创建销毁线程的场景
首先,我们先来了解一下什么是池:
池
假设一个美女,有很多人追,美女就从这些人里面挑了一个她最喜欢的交往,交往一段时间之后,美女她腻了,她想换一个男朋友,那她接下来就要干两件事
1:想办法和这个男的分手,需要一些技巧,比如挑他毛病,作 之类的
2:再找一个小哥哥,培养感情,然后交往
但是这样的效率就比较低啦,有没有什么办法来提高一些效率呢?
当然有,只有美女在和前一个交往的时候,和另一个或多个小哥哥,搞暧昧(养🐟),把他们都放到自己的鱼塘里,那和上一个分手,下一个男朋友来的就很快了。
我们还是只做了两步,只是把第二步提前了。
那“鱼塘”里的这些人,我们通常叫他们 —— “备胎”
那这个“鱼塘” 就 可以看成 “池” ,来放“备胎”
同样的,线程池,就是在使用第一个线程的时候,提前把 2,3,4,5...(多个)线程创建好(相对于前面的培养感情),那后续我们想使用新的线程的时候,就不必重新创建了,直接拿 池 里的线程用就行了。(此时创建线程的开销就被降低了)
为什么,从池子里取的效率比新创建线程效率高?
这是因为,从池子里取 这个动作,是存粹的 用户态 的操作,而创建新的线程,这个动作,则是需要 用户态 + 内核态 相互配合完成的操作。
内核态 和 用户态
如果一段程序,是在系统内核中执行的,此时就称为“内核态” ,如果不是,则称为“用户态”
操作系统,是由 内核 + 配套的应用程序 构成的,
内核:系统最核心的部分
创建线程操作,就需要调用系统 api,进入到内核中,按照内核态的方式来完成一系列动作。
内核态的操作要比 纯用户态的操作开销要更大 :至于为什么,我们来举一个例子解释一下:
银行办业务的例子
首先这个来办理业务的人他不能 进入柜台后面,只能在大厅里,
这个人想来办张银行卡,需要身份证复印件,但是这个人他忘带了,那此时柜台的服务人员就给了他两个选择:
1:把身份证给她,她去帮他复印
2:大厅的角落,有一个自助复印机,他可以去那里自己复印
那这两个选择中的第二个,自己复印就是纯 用户态操作(这个人可以立即去复印,完事后立即回来办理业务,整个过程非常利落,非常可控)
但是如果交给 柜台的服务人员(第一个选择),这个过程就涉及到 内核态 操作了,那此时,你把东西交给他俩,你也不知道柜员消失之后去做了那些事情,也不是的她啥时候回来,整个过程是不可控的。
操作系统内核,是要给所有的进程提供服务的,当你要创建线程的时候,内核虽然会帮你做,但是做的过程中难免也要做一些其他的事情。那在你这边的结果,就不是那么可控。
上述就是内核态 和 用户态的区别 。
Java标准库中的线程池
线程池的创建
我们发现了,线程池这个对象不是我们直接 new 的,而是通过一个专门的方法,返回了一个线程池的对象。
这种写法就涉及到了 “工厂模式”(校招常考的设计模式)(和上一篇介绍的 单例模式 并列)
工厂模式
工厂模式的作用?
通常我们创建对象 都是使用 new,new 关键字就会触发 类的构造方法,但是构造方法,存在一定的局限性。
“工厂模式” 就是给 构造方法填坑的。
那 “工厂模式” 具体是填的什么 坑 呢,我们举一个例子:
假设 考虑 一个类,来表示平面上的点
然后我们给这个类提供构造方法:
第一个构造方法:
期待使用笛卡尔坐标系来构造对象。
第二个构造方法:
使用极坐标来构造对象
但是编译失败了。
原因:
很多时候,我们希望构造一个对象,可以有多种构造方式 。那多种方式,我们就需要使用多个版本的构造方法来分别实现,但是构造方法要求方法的名字必须是类名,不同的构造方法 只能通过 重载 的方式来区分了,而重载又要求 参数类型 或 个数 不同。
而上面的两个构造方法 很明显没有构成 重载,当然会编译失败。
这就是 构造方法的局限性 。
那“工厂模式”就能解决上述问题 :
使用普通的方法,代替构造方法完成初始化工作,普通的方法就可以使用方法的名字来区分了。也就不受 重载的规则制约了。
工厂模式实践
在实践中,我们一般单独 搞一个类,然后给这个类搞一些静态方法,由这些静态方法负责构造出对象
伪代码
class PointFactory {public static Point makePointByXY(double x, double y) {Point point = new Point();point.setX(x);point.setY(y);return p;}public static Point makePointByRA(double r, double a) {//和上边类似} } class Demo {public static void main(String[] args) {//使用 Point p = PointFactory.makePointByXY(10,20); } }
上述介绍之后,我们就知道了为啥 线程池 的 对象我们不直接 new 了
这种方法就是 工厂模式
不同的几种线程池
第一种:
此时构造出的线程池对象,有一个基本特点,线程数目是能够动态适应的。
cached: 缓存,用过之后不着急释放,先留着以备下次使用。
也就是说,随着往线程池里添加任务,这个线程池中的线程会根据需要自动被创建出来,创建出来之后也不会着急销毁,会在池子里保留一定的时间,以备随时再使用。
除了上边的线程池,我们还有其他的线程池:
第二种 :
这个方法就需要我们指定 创建几个线程,线程个数是固定的 (Fix:固定)
第三种:
只有单个线程的线程池:
第四种 :
类似于 定时器, 只是 不是只有一个 扫描线程 负责执行任务了,而是有多个线程执行时间到的任务.
第一种和第二种常用
上述这几个工厂方法生成的线程池,本质上都是对 一个类进行的封装 —— ThreadPoolExector
ThreadPoolExector 这个类的功能十分丰富,它提供了很多参数,标准库中上述的几个工厂方法,其实就是给这个类填写了不同的参数来构造线程池。
ThreadPoolExector 的使用方式
ThreadPoolExector 的核心方法:
1.构造方法
2.注册任务(添加任务)
注册任务(简单):submit
⁜⁜ 构造方法⁜⁜【经典面试题】
构造方法中的参数,很多,且重要,
我们打开Java文档 Overview (Java Platform SE 8 ) (oracle.com)
打开这个包 juc —— 这个包里放的试和 “并发编程” 相关的内容(Java中,并发编程最主要的体现形式就是多线程)
点进这个包然后往下找:
然后我们直接翻到构造方法 :
上面的四个构造方法,都差不多,就是参数个数 不一样,第四个 参数最多,能够涵盖上述的三个版本。
所有我们重点看第四个构造方法:
这一组参数,描述了线程池中,线程的数目:
这个线程池里的线程 的数目试可以动态变化的,
变化的范围就是【corePoolSize, maximumPoolSize】
那 “核心线程” 和 “最大线程” 如何理解呢?
如果把一个线程池,理解为一个公司,此时,公司里有两类员工
1.正式员工
2.实习生
那正式员工的数目,就是核心线程数,正式员工 + 实习生的数目就是最大线程数
正式员工和实习生的区别:
正式员工,允许摸鱼,不会因为摸鱼被公司开除,有劳动法罩着。
但是实习生,不允许摸鱼,如果这段时间任务多了,此时,就可以多搞几个实习生去干活,如果过段时间任务少了,并且这样的状态还持续了一定时间,那空闲的实习生就可以裁掉了。
这样做,既可以满足效率的要求,又可以,避免过多的系统开销 。
ps:
使用线程池,需要设置线程的数目,数目设置多少合适?
一定不是一个具体的数字!!!因为在接触到实际的项目代码之前,这个数目是无法确定的!!!
一个线程 执行的代码,主要有两类:
1.cpu 密集型:代码里主要的逻辑是在进行 算术运算/逻辑判断。
2.IO 密集型:代码里主要进行的是IO操作。
—— 假设一个线程的所有代码都是 cpu 密集型代码,这个时候,线程池的数量就不应该超过N,就算设置的比N大,此时也无法提高效率,因为cpu吃满了。
—— 假设一个线程的所有代码都是 IO 密集型代码,这个时候不吃cpu,此时设置的线程数,就可以是超过N,(一个核心可以通过调度的方式来并发执行)
上述,我们就知道了,代码不同,线程池的线程数目设置就不同,我们无法知道一个代码,具体多少内容是cpu密集,多少内容是IO密集。所以我们无法确定 数目设置多少合适。
正确做法:使用实验的方式,对程序进行性能测试,测试的过程中尝试修改不同的线程池的线程数目,看那种情况,更符合要求。
这一组参数,描述了允许实习生摸鱼的时间,(实习生不是 一摸鱼就马上被开除)
这个参数的意思是 阻塞队列 ,用来存放线程池里的任务。
可以根据需要,灵活设置这里的队列是啥,比如需要优先级, 就可以设置 PriorityBlockingQueue
如果不需要 优先级,并且任务数目是相对恒定的,可以使用 ArayyBlockingQueue,如果不需要优先级,并且任务数目变动比较大,就可以用 LinkedBlockingQueue
这个参数就是 工厂模式的体现 ,此处使用 ThreadFactory 作为 工厂类 由这个类负责创建线程
使用工厂类来创建线程,主要是为了在创建线程的过程中,对线程的属性做出一些设置。
如果手动创建线程,就得手动设置这些属性,就比较麻烦,使用工厂方法封装一下,就更方便。
下面这个参数是最重要的 ,是线程池的拒绝策略
一个线程池,能容纳的任务数量,有上限,当持续往线程池里添加任务的时候,一旦达到了上限,还继续添加,会出现什么效果?
拒绝策略就是来解决这个问题的: 不同的拒绝策略有不同的效果。
上面的这四个就是不同的拒绝策略
如果队列满了,再添加就直接抛出异常
新添加的任务,由添加任务的线程负责执行
丢弃最老的任务
丢弃当前新加的任务
实现一个简单的线程池
这个代码比较简单,就不多说了,代码里都有注释
import java.awt.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** @Author: iiiiiihuang*/
public class ThreadPool {//任务阻塞队列private BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(4);//通过这个方法,把任务添加到队列中public void submit(Runnable runnable) throws InterruptedException {//此处的拒绝策略,相当于第五种策略,阻塞等待(下策)queue.put(runnable);}//构造方法public ThreadPool(int n) {//创建出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();}}
}
关注,点赞,评论,收藏,支持一下╰(*°▽°*)╯╰(*°▽°*)╯
相关文章:
(JavaEE)(多线程案例)线程池 (简单介绍了工厂模式)(含经典面试题ThreadPoolExector构造方法)
线程诞生的意义,是因为进程的创建/销毁,太重了(比较慢),虽然和进程比,线程更快了,但是如果进一步提高线程创建销毁的频率,线程的开销就不能忽视了。 这时候我们就要找一些其他的办法…...
单播与多播mac地址
MAC 地址(Media Access Control Address)是一个用于识别网络设备的唯一标识符。每个网络设备都有一个独特的 MAC 地址,用于在局域网中进行通信。 单播MAC地址:单播MAC地址用于单播通信,即一对一的通信模式。当设备发送…...
反向动力学Ik学习
参考文章:(非本人原创) 英文原文:Inverse Kinematics Techniques in Computer Graphics: A Survey (andreasaristidou.com) 知乎翻译文章: 【游戏开发】逆向运动学(IK)详解 - 知乎 (zhihu.co…...
基于Levenberg-Marquardt算法的声源定位matlab仿真
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 .................................................................... %ML if (bML1)varxs…...
vscode的Emmet语法失效
解决方案:设置 -> 搜索Emmet -> 勾选 Emmet:Trigger Expansion On Tab和Emmet: Use Inline Completions -> 重启 注:Emmet语法是vscode自带的语法,可以快速生成HTML结构/CSS样式/格式化语法 Emmet语法 参考:关于vscode使…...
堆排序(大根堆)
堆的定义如下,n个关键字序列[1...n]称为堆,当且仅当满足: a(i)>a(2i)且a(i)>a(2i1) 这个为大根堆 a(i)<a(2i)且a(i)<a(2i 1) 这个为小根堆 通过建堆得到大根堆 大根堆 87,45,78,32,17,65,53,9 可以看成 …...
Mybatis学习笔记3 在Web中应用Mybatis
Mybatis学习笔记2 增删改查及核心配置文件详解_biubiubiu0706的博客-CSDN博客 技术栈:HTMLServletMybatis 学习目标: 掌握mybatis在web应用中如何使用 Mybatis三大对对象的作用域和生命周期 关于Mybatis中三大对象的作用域和生命周期、 官网说明 ThreadLocal原理及使用 巩…...
软件测试之功能测试详解
一、功能测试概述 1)功能测试就是对产品的各功能进行验证,根据功能测试用例,逐项测试,检查产品是否达到用户要求的功能。 2)功能测试,根据产品特性、操作描述和用户方案,测试一个产品的特性和…...
javascript选取元素的范围,可以包含父级,也可以不包含父级
//函数可以选取元素的范围,对于要选取文本的非常方便,或选取特定的子节点 function getRange(element){//判断是否支持range范围选取var supdocument.implementation.hasFeature("Range","2.0");var also(typeof document.createRan…...
QGIS怎么修改源代码?持续更新...
修改配置文件保存位置 修改目的:放着和本地安装的其他QGIS共用一份配置文件 修改文件:core/qgsuserprofilemanager.cpp 修改位置:第37行 return basePath QDir::separator() "my_profiles";修改完毕后,再次生成一下…...
dev board sig技术文章:轻量系统适配ARM架构芯片平台
摘要:本文简单介绍OpenHarmony轻量系统移植,会分多篇 适合群体:想自己动手移植OpenHarmony轻量系统的朋友 开始尝试讲解一下系统的移植,主要是轻量系统,也可能会顺便讲下L1移植。 1.1移植类型 OpenHarmony轻量系统的…...
MyBatis之增删查改功能
文章目录 一、创建各种类二、MyBatis的各种功能 1、查询<select>2、增加<insert>3、修改<update>4、删除<delete>三、总结 前言 在MyBatis项目中编写代码实现对MySql数据库的增删查改 一、创建各种类 1、在Java包的mapper文件下创建一个接口 我创建…...
Leetcode算法入门与数组丨5. 数组二分查找
文章目录 1 二分查找算法2 二分查找细节3 二分查找两种思路3.1 直接法3.2 排除法 1 二分查找算法 二分查找算法是一种常用的查找算法,也被称为折半查找算法。它适用于有序数组的查找,并通过将待查找区间不断缩小一半的方式来快速定位目标值。 算法思想…...
拓扑关系如何管理?
在设备对接涂鸦的云端过程中,一部分设备由于自身资源或硬件配置,无法直接连接云端。而是需要通过网关进行中转,由网关代理实现和云端进行数据交互,间接实现设备接入云端。这样的设备也称为子设备。 要想实现网关代理子设备接入云…...
vue的由来、vue教程和M-V-VM架构思想、vue的使用、nodejs
vue vue的由来 vue教程和M-V-VM架构思想 vue的初步简单使用 nodejs vue的由来 # 1 HTML(5)、CSS(3)、JavaScript(ES5、ES6、ES11):编写一个个的页面 -> 给后端(PHP、Python、Go、Java) -> 后端嵌入模板语法 -> 后端渲染完数据 -> 返回数据给前端 ->…...
课程表 循环依赖 拓扑排序 go语言
学会拓扑排序题目的基本解法 res数组 记录上课顺序g 记录学了课程i 能解锁的课程jindeg 记录每个课程的入度q 记录入度为0的课程 for循环q去解放其他课程 本题来自力扣课程表 func findOrder(numCourses int, prerequisites [][]int) []int {res : []int{}//建一个二维数组记…...
【红包雨接口设计】
一、服务器地址 http://rb.atguigu.cn 二、公共请求头参数 参数名称类型是否必选描述tokenString是用户唯一标识 备注:为了方便我们今天演示,服务端接受所有token。 三、接口 1. 创建红包雨 请求方式:GET请求地址:/api/v1/se…...
SSL证书到期更换证书会影响排名吗?
在现代的数字化时代,网络安全和用户体验成为了网站运营商和开发者们需要高度关注的问题。SSL证书作为一种重要的安全协议,对网站的安全性和用户信任起着至关重要的作用。然而,随着SSL证书的有效期限届满,许多网站运营商面临着更换…...
前端常用库之-JavaScript工具库lodash
文章目录 前端常用库之-JavaScript工具库lodash一、什么是lodash二、安装三、lodash使用Lodash 的 pick() 函数介绍和使用react 实例demo:pick结合...展开运算符(spread operator) 前端常用库之-JavaScript工具库lodash 一、什么是lodash 官网: https:…...
Linux- execve()
execve() 是 Linux/UNIX 中的 exec 函数家族中的一个,它允许进程执行一个新的程序。具体地,execve() 替换当前进程的映像为新的程序映像。 函数原型如下: int execve(const char *pathname, char *const argv[], char *const envp[]);pathn…...
007 数据结构_堆——“C”
前言 本文将会向您介绍关于堆Heap的实现 具体步骤 tips:本文具体步骤的顺序并不是源代码的顺序 typedef int HPDataType; typedef struct Heap {HPDataType* _a;int _size;int _capacity; }Heap;初始化 void HeapCreate(Heap* hp, HPDataType* a, int n) {hp-&…...
zabbix的原理与安装
一、Zabbix介绍 1、zabbix 是什么? zabbix是一个开源的IT基础监控软件,能实时监控网络服务,服务器和网络设备的状态,如网络使用,CPU负载、磁盘空间等,主要是包括数据的收集、报警和通知的可视化界面zabbi…...
ReactNative中升级IOS 17版本Crash解决
ReactNative中升级IOS 17版本Crash解决 ReactNative中升级IOS 17版本Crash解决一、问题描述二、原因分析三、解决方案决策3.1 设置宽高为非零值3.2 使用新的UIGraphicsImageRenderer替换就版本的UIGraphicsBeginImageContext 四、可能使用到该API的三方库4.1 react-native-fast…...
MongoDB详解
一、MongoDB概述 MongoDB 是一个基于 分布式文件存储 的开源 NoSQL 数据库系统,由 C 编写的。MongoDB 提供了 面向文档 的存储方式,操作起来比较简单和容易,支持“无模式”的数据建模,可以存储比较复杂的数据类型,是一…...
【SpringCloud微服务全家桶学习笔记-服务注册zookeeper/consul】
SpringCloud微服务全家桶学习笔记 Eureka服务注册 gitee码云仓库 9.其他服务注册框架 (1)zookeeper安装与使用 zookeeper需安装在虚拟机上,建议使用CentOS,安装地址如下: zookeeper镜像源 选择第一个进入后下载ta…...
【滑动窗口】LCR 016. 无重复字符的最长子串
LCR 016. 无重复字符的最长子串 解题思路 窗口内的字符串就是不重复子串每次遇到新的字符 看看窗口内是否存在该字符 如果存在直接剔除 然后调整窗口左边界不存在 添加窗口内部 右边界 class Solution {public int lengthOfLongestSubstring(String s) {if(s.length() < …...
C++中将类成员函数作为变量传递给函数
假设类ClassName有一个成员函数 void ClassName::funcname(int);通过typedef定义一个类成员函数指针类型,参数和返回值类型都要与成员函数对应 typedef void (ClassName::*FuncPtr)(int); // 定义类成员函数指针获取到的参数就是 FuncPtr pf...
2024届数字IC设计秋招面经-鼎信
背景 985硕士,计算机科班,实验室做cpu设计和fpga算法加速,我做处理器安全方向,有项目。 投递 8.25 没有笔试,两轮面试,直接通知下周一面试,草草的准备了下。 一面 技术面 9.4 不到半小时 …...
【数据结构】二叉树的节点数,叶子数,第K层节点数,高度,查找x节点,判断是否为完全二叉树等方法
💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤 📃个人主页 :阿然成长日记 …...
前馈神经网络(FFNN)和多层感知机(MLP)
多层感知器(MLP, Multi-Layer Perceptron)和前馈神经网络(Feed-Forward Neural Network, FFNN)是深度学习中两个经常被使用的术语,它们经常被互换使用。让我们详细地了解这两个术语: 多层感知器 (MLP): M…...
wordpress改网页电话/软件培训机构排行榜
【小白从小学Python、C、Java】 【Python-计算机等级考试二级】 【Python-数据分析】 定义一个方法,作用到对象上获取对应索引的值 operator.itemgetter() [太阳]选择题 请问对以下Python代码说法错误的是? from operator import itemgetter #定义方法&a…...
建设一个怎样的自己的网站/网络营销方法有哪些举例
我叫张立新,喜欢音乐。在本次团队项目中担任后期测试人员转载于:https://www.cnblogs.com/Zlxz/p/7495130.html...
做点阵纸的网站/腾讯企业邮箱
1.定义: 模块:用来从逻辑上组织python代码(变量、函数、类、逻辑), 本质就是一个.py结尾的python文件。(文件名test.py,对应的文件名test.) 包:用来从逻辑上组织模块的&a…...
重庆建企业网站/上海比较大的优化公司
关于如何获取webrtc的源码,请参考Webrtc代码下载这篇文章。 构建android编译环境 $ cd src/ $ source ./build/android/envsetup.sh $ export JAVA_HOME/usr/bin/ $ export GYP_DEFINES"$GYP_DEFINES OSandroid" $ export GYP_GENERATORSninja 下载编译所…...
网站用社交图标做链接侵权吗/百度浏览器官方网站
911. 在线选举 给你两个整数数组 persons 和 times 。在选举中,第 i 张票是在时刻为 times[i] 时投给候选人 persons[i] 的。 对于发生在时刻 t 的每个查询,需要找出在 t 时刻在选举中领先的候选人的编号。 在 t 时刻投出的选票也将被计入我们的查询之…...
建设网站需要什么资质/宁波seo优化公司排名
本篇介绍easymybatis如配置乐观锁和逻辑删除。乐观锁easymybatis提供的乐观锁使用方式跟JPA一样,使用Version注解来实现。即:数据库增加一个int或long类型字段version,然后实体类version字段上加上Version注解即可。实现原理是根据mysql的行锁…...