【JavaEE精炼宝库】多线程(6)线程池
目录
一、线程池的概念及优势
1.1 线程池的概念:
1.2 线程池的优势:
二、工厂模式
三、标准库中的线程池
3.1 标准库线程池参数解释:
3.1.1 corePoolSize | maximumPoolSize:
3.1.2 keepAliveTime | unit:
3.1.3 workQueue:
3.1.4 ThreadFactory:
3.1.5 handler:
3.2 创建线程池演示:
3.3 利用线程池的工厂类来创建线程池:
3.4 线程池的使用演示:
四、线程池的实现
一、线程池的概念及优势
1.1 线程池的概念:
线程池是一种并发编程的技术,它维护着一组预先创建的线程,以便在需要时重用它们来执行多个任务。这可以提高程序的性能和效率,因为线程的创建和销毁通常是昂贵的操作,通过重用线程,可以减少这种开销。
在我们前面已经介绍了线程比进程更加轻量,在频繁创建销毁的时候,线程更有优势。但是随着时代的发展,对于 “频繁” 有了新的定义(比如之前 1 万就认为是频繁,但是现在可能 1000 万才认为是频繁)。即使是线程,在如此频繁的创建和销毁的情况下,开销也变得难以接受。
那么要怎么优化呢?
答:我们可以通过线程池和协程进行优化。
协程这里我们暂且不进行描述,Go 语言主打的卖点就是使用协程处理并发编程。线程池的优化我们马上讲到。
1.2 线程池的优势:
• 降低资源消耗:
减少线程的创建和销毁带来的性能开销。
• 提高响应速度:
当任务来时可以直接使用,不用等待线程创建
• 可管理性:
进行统一的分配,监控,避免大量的线程间因互相抢占系统资源导致的阻塞现象。
二、工厂模式
在下面即将介绍的线程池参数中,其中有一个参数是工厂类。所以在这里提前给大家解释一下什么是工厂模式。
工厂模式:也是一种设计模式,主要解决的问题是构造方法创建对象太坑了的问题。为什么说构造方法创建对象有坑呢?举个栗子:
因为在 Java 的语法中要求构造方法的命名必须是类名且参数类型和个数,排列顺序不能一样。这就导致遇到上面这种案例解决不了。就需要工厂模式,核心思路,不使用构造方法创建对象,给构造方法包装一层。例如下面这段代码:这只是个伪代码,看看思路就行,makePointByXY 这样的方法就叫 “工厂方法”,这样写代码的套路就叫做 “工厂模式”,没有什么特别的。
public class Point {public static Point makePointByXY(double x,double y){Point p = new Point();p.setX(x);//和 setR 是有区别的,比如传给的函数不一样p.setY(y);return p;}public static Point makePointByRA(double r,double a){Point p = new Point();p.setR(r);p.setA(a);return p;}
}
工厂类的写法如下图: 工厂方法用单独的类来进行提供。
如果语法层面上,不强制要求构造方法名字必须和类名一致,就没有上述模式的必要了。
三、标准库中的线程池
3.1 标准库线程池参数解释:
ThreadPoolExecutor:
使用起来比较复杂,在其构造方法中,包含很多的参数。(面试考点)例如下面这个就是包含全部参数的构造方法,了解这个了,其他的就都会了,下面会围绕这个构造方法参数进行解释。
3.1.1 corePoolSize | maximumPoolSize:
在 Java 的标准库中线程池把线程分成两类:1. 核心线程。2. 非核心线程。
顾名思义 corePoolSize 就是核心线程数,maximumPoolSize 就是最大线程数(核心线程数 + 非核心线程数)。
这里就涉及到动态扩展的概念,在线程池刚开始就会创建出核心线程数的线程(最小线程数),随着项目的进行,如果添加的任务比较多的话,核心线程数处理不过来了(有很多任务在队列中,排队等待执行),这个时候线程池就会自动的创建出新的线程,来支撑更多的任务。但是,创建出来的线程总数不能超过最大线程数(maximumPoolSize),过了一段时间,任务没有那么多了,线程 “清闲下来” 这时部分非核心线程就会被释放掉,线程池中会保证线程数目不少于核心线程数。
这样就既可以保证任务多的时候的效率,也能保证任务少的时候,系统的开销。到这里就又有了一个问题:
在实际开发中,线程数应该设置成多少合适呢?
答:只要是具体的数字,答案一定是错误的。我们要根据实验的方式,对程序进行性能测试,测试过程中,设定不同的线程池的数值。最终根据实际程序的响应速度和系统开销,综合权衡,找到一个合适值。
这不仅仅和电脑的配置有关(有多个 CPU 核心),更重要的是,和我们的程序的实际特点有关系。极端情况下可以分为两大类:
• CPU 密集型程序:代码完成的逻辑,都是要通过 CPU 来完成的.(此时的性能瓶颈是 CPU)。此时线程的数量是不应该超过 CPU 逻辑核心数的。
• IO 密集型程序:代码大部分时间在等待 IO(等待 IO 是不消耗 CPU,不参与调度)。此时瓶颈就不在 CPU 上,我们更多考虑其他方面(比如网络程序,要考虑网卡宽带的瓶颈)。
3.1.2 keepAliveTime | unit:
• keepAliveTime:
表示非核心线程,允许空闲的最大时间。也就是说非核心线程在线程池 ”不忙“ 的时候不是马上回收掉。
• unit:
TimeUnit:这是一个枚举类型。其中的枚举常量有:
3.1.3 workQueue:
阻塞队列我们上一篇文章已经自己实现过了,这里利用阻塞队列要存储 Runnable 对象也就是要执行的任务。线程池会提供 submit 方法,让其他线程把任务提交给线程池。其他线程会 submit 任务到这个队列中,这个队列存的元素就是 Runnable 对象,要执行的逻辑就是 run 方法里面的内容。
3.1.4 ThreadFactory:
ThreadFactory:是标准库中提供的,用来创建线程的工厂类。这个线程工厂,主要是为了批量的给创建的线程设置一些属性, 线程工厂在它的工厂方法中,把线程的属性提前初始化好了。至于什么是工厂类,在上面的工厂模式那里已经解释过了,这里就不再赘述。
3.1.5 handler:
面试官问线程池的参数都是什么意思?其实就是在考我们对于这个参数的理解,前面 6 个都是添头。注意:此处 Handler 不是句柄的意思(句柄的术语也是 Handler)。
这里可以理解为拒绝策略,如果当前任务队列满了,仍要继续添加任务,咋办呢?直接阻塞其实不太合适。所以这里多了一个拒绝策略的参数。
幸运的是,我们 Java 的大佬已经给我们实现了四种常用的拒绝策略,在 ThreadPoolExecutor 类的里面,有四种静态内部类,列举如下:
对上面四种拒接策略的简单翻译如下:
• AbortPolicy():超过负荷,直接抛出异常。
• CallerRunsPolicy():调用者负责处理多出来的任务。
• DiscardOldestPolicy():丢弃队列中最老的任务。
• DiscardPolicy():丢弃新来的任务。
3.2 创建线程池演示:
这里唯一有些难度的就是拒绝策略的创建,在 ThreadPoolExecutor 类中有四个内部类实现了 RejectedExecutionHandler 接口。具体代码如下:
简单的编写如下:
import java.util.concurrent.*;
public class demo3 {public static void main(String[] args) {BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(5);//阻塞队列ThreadFactory factory = new ThreadFactory() {//工厂模式@Overridepublic Thread newThread(Runnable r) {return new Thread(r);//这里就可以对 Thread 设置一些参数}};RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();//拒绝策略,注意这里是调用 ThreadPoolExecutor 的静态内部类ThreadPoolExecutor pool = new ThreadPoolExecutor(3,6,100,TimeUnit.DAYS,queue,factory,handler);}
}
3.3 利用线程池的工厂类来创建线程池:
使用线程池的工厂类来创建线程池的方法一共有 6 种,其中我们重点理解下面给出的四种即可。
工厂类为:Executors ,这里产生的线程池为 ExecutorService 类。具体如下图:
可以看到官方提供的创建线程池的工厂方法还是很多的。我们主要理解下面这四个即可。
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public class demo1 {public static void main(String[] args) {//能够根据任务数量,自动进行线程扩容的线程池ExecutorService sevice1 = Executors.newCachedThreadPool();//创建固定线程数目的线程池ExecutorService service2 = Executors.newFixedThreadPool(4);//创建只包含单个线程的线程池ExecutorService service3 = Executors.newSingleThreadExecutor();//创建固定数目的线程个数,但是任务延时执行的线程池ExecutorService service4 = Executors.newScheduledThreadPool(4);}
}
Executors 创建线程池的几种方式:
• newFixedThreadPool:创建固定线程数的线程池。
• newCachedThreadPool:创建线程数目动态增长的线程池。
• newSingleThreadExecutor:创建只包含单个线程的线程池。
• newScheduledThreadPool:设定延迟时间后执行命令,或者定期执行命令。
Executors 本质上是 ThreadPoolExecutor 类的封装。
3.4 线程池的使用演示:
我们可以利用 submit 来进行添加任务(每个都必须实现 Runnable 接口)。
import java.util.concurrent.*;
public class demo1 {public static void main(String[] args) {ExecutorService service1 = Executors.newCachedThreadPool();for(int i = 1;i <= 1000;i++){int id = i;//这里必须要使用 id 来接受 iservice1.submit(new Runnable() {//匿名内部类@Overridepublic void run() {System.out.println(id +" , " + Thread.currentThread().getName());//必须只能打印 id,打印 i 会报错//报错的原因为:变量捕捉}});}}
}
案例演示的结果如下:
至于剩下三个线程池都是一样的,这里就不过多赘述了。 如果只是简单的使用一下,就直接用工厂类即可,如果希望更加精细的控制,就使用原生的 ThreadPoolExecutor。所以 Java 标准库线程池的创建方式一共有 7 种。
四、线程池的实现
为了更加深入的理解线程池的使用,这里我们就来简单的实现一个简易的线程池。我们要实现的东西有1. 若干线程。2. 任务队列。3. 提供 submit 方法。
我们可以利用 ArrayList 来存储线程,利用阻塞队列来辅助完成线程池,创建一个能自动扩容的线程池,每个线程的任务就是不停的去阻塞队列中取元素然后执行。剩下的一些在代码中都有注释。
代码实现如下:
import java.util.concurrent.*;
import java.util.*;
//自己实现一个线程池
class MyThreadPool{//1. 线程private int corePoolSize;//核心线程数private int maximumPoolSize;//最大线程数private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(1000);private List<Thread> threads = new ArrayList<>();//存储线程public MyThreadPool(int coreThreadSize,int maximumPoolSize) throws InterruptedException {this.corePoolSize = coreThreadSize;this.maximumPoolSize = maximumPoolSize;for(int i = 0;i < coreThreadSize;i++){Thread t = newThread();t.start();threads.add(t);}}private Thread newThread(){//每个线程的任务就是不停的执行队列中的元素Thread t = new Thread(() -> {while(true){try {Runnable task = queue.take();task.run();} catch (InterruptedException e) {throw new RuntimeException(e);}}});return t;}//2. 阻塞队列//3. submit 方法public void submit(Runnable task) throws InterruptedException {queue.put(task);//如果队列中的元素大于一个阈值,且线程数小于最大线程数时,就可以创建新的线程来帮助,处理队列中的元素if(queue.size() >= 50 && threads.size() < maximumPoolSize){//创建 线程Thread t = newThread();t.start();threads.add(t);}}
}
public class demo3 {public static void main(String[] args) throws InterruptedException {MyThreadPool threadPool = new MyThreadPool(4,50);for(int i = 0;i < 10000;i++) {int id = i;threadPool.submit(new Runnable(){@Overridepublic void run(){System.out.println("hello " + id + " ," + Thread.currentThread().getName());}});}}
}
具体效果如下:
如果要实现拒绝策略的话,也是在 submit 这里实现。
结语:
其实写博客不仅仅是为了教大家,同时这也有利于我巩固知识点,和做一个学习的总结,由于作者水平有限,对文章有任何问题还请指出,非常感谢。如果大家有所收获的话还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。
相关文章:
【JavaEE精炼宝库】多线程(6)线程池
目录 一、线程池的概念及优势 1.1 线程池的概念: 1.2 线程池的优势: 二、工厂模式 三、标准库中的线程池 3.1 标准库线程池参数解释: 3.1.1 corePoolSize | maximumPoolSize: 3.1.2 keepAliveTime | unit: 3.1…...
数据仓库和数据库的区别
数据仓库和数据库在许多方面存在显著的区别,主要体现在数据的用途、架构、设计原则和性能优化上。以下是两者之间的详细区别: 1. 目的和用途 数据库(Database): 主要用途:用于日常业务操作和事务处理。数据…...
芯片验证分享7 —— 代码审查1
大家好,我是谷公子,前几节课给大家分享了如何设计激励,今天我们来如何进行代码审查。 之前讨论的是基于计算机的验证技术,现在讨论非基于计算机的验证过程(即“人工验证”,或代码审查)。代码审查在查找错误方面非常有…...
Shell脚本从入门到实战
一、概述 shell 是一个命令行解释器,它接受应用程序、用户命令,然后调用操作系统内核。 shell 还是一个功能强大编程语言,易调试,易编写,灵活性强。 二、mac 怎么重启docker 1.如何重启 Docker on Mac 在 macOS 上…...
使用 python 将 Markdown 文件转换为 ppt演示文稿
在这篇博客中,我们将展示如何使用 wxPython 创建一个简单的图形用户界面 (GUI),以将 Markdown 文件转换为 PowerPoint 演示文稿。我们将利用 markdown2 模块将 Markdown 转换为 HTML,并使用 python-pptx 模块将 HTML 内容转换为 PowerPoint 幻…...
Arnoldi Iteration 思考
文章目录 1. 投影平面2. Arnoldi Iteration3. python 代码 1. 投影平面 假设我们有一个向量q,我们需要关于向量q,构建一个投影平面P,使得给定任何向量v,可以通过公式 p P v pPv pPv,快速得到向量v在投影平面P上的投影向量p. 计算向量内积,…...
【Kafka】SpringBoot整合Kafka详细介绍及代码示例
Kafka介绍 Apache Kafka是一个分布式流处理平台。它最初由LinkedIn开发,后来成为Apache软件基金会的一部分,并在开源社区中得到了广泛应用。Kafka的核心概念包括Producer、Consumer、Broker、Topic、Partition和Offset。 Producer:生产者&a…...
C++ 质数因子分解
描述 功能:输入一个正整数,按照从小到大的顺序输出它的所有质因子(重复的也要列举)(如180的质因子为2 2 3 3 5 ) 输入描述: 输入一个整数 输出描述: 按照从小到大的顺序输出它的所有质数的…...
laravel版本≥ 8.1
laravel10 php ≥ 8.1 且 ≤ 8.3? 8.1 < php < 8.3PHP版本要求在 8.1 到 8.3 之间,包括这两个版本。具体来说:"≥ 8.1" 表示 PHP 的版本至少是 8.1,也就是说 8.1 及以上的版本都可以。 "≤ 8.3" 表示 P…...
【iOS】MRC下的单例模式批量创建单例
单例模式的介绍和ARC下的单例请见这篇:【iOS】单例模式 目录 关闭ARC环境MRC下的单例ARC下的单例批量创建单例Demo 关闭ARC环境 首先关闭ARC环境,即打开MRC: 或是指定某特定目标文件为非ARC环境: 双击某个类文件,指定…...
计算机网络期末复习
今天考专四,环境都蛮好的,试卷也很新,老师人也不错,明年再来。 又到期末考试咯,大家复习没有?还没复习啊?还不复???? 目录 第一章 1-02 试简述…...
python写一个获取竞品信息报告
要编写一个获取竞品信息报告的Python程序,首先需要明确您想要获取的竞品信息以及数据来源。在这个示例中,我将展示如何从网页提取竞品信息,并编写一个简单的报告。 假设您想要获取以下竞品信息: 1. 产品名称 2. 产品价格 3. 产品特…...
一文彻底理解机器学习 ROC-AUC 指标
在机器学习和数据科学的江湖中,评估模型的好坏是非常关键的一环。而 ROC(Receiver Operating Characteristic)曲线和 AUC(Area Under Curve)正是评估分类模型性能的重要工具。 这个知识点在面试中也很频繁的出现。尽管…...
【二】【动态规划NEW】91. 解码方法,62. 不同路径,63. 不同路径 II
91. 解码方法 一条包含字母 A-Z 的消息通过以下映射进行了 编码 : ‘A’ -> “1” ‘B’ -> “2” … ‘Z’ -> “26” 要 解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法ÿ…...
Python闯LeetCode--第3题:无重复字符的最长子串
Problem: 3. 无重复字符的最长子串 文章目录 思路解题方法复杂度Code 思路 一上来马上想到两层for循环暴力枚举,但是又立马想到复杂度是 O ( n 2 ) O(n^2) O(n2),思考了一下能否有更优解,于是想到用头尾两个指针来指定滑动窗口(主…...
HTML DOM 对象
HTML DOM 对象 1. 概述 HTML DOM(文档对象模型)是一个跨平台和语言独立的接口,它允许程序和脚本动态地访问和更新文档的内容、结构和样式。在HTML DOM中,文档被表示为节点树,其中每个节点代表文档中的一个部分,例如元素、文本或属性。HTML DOM对象是构成这个节点树的基…...
如何解决 BeautifulSoup 安装问题:从 BeautifulSoup 3 到 BeautifulSoup 4
在使用 Python 的过程中,解析 HTML 和 XML 数据是一项常见任务。BeautifulSoup 是一个非常流行的解析库。然而,最近在安装 BeautifulSoup 时,遇到了一些问题。本文将介绍如何解决这些问题,并成功安装 BeautifulSoup 4。 问题描述 …...
原型模式--深复制/浅复制
原型模式用于克隆复杂对象,由于new一个实例对象会消耗大部分时间,所以原型模式可以节约大量时间 1 public class Sheep implements Cloneable{2 private String name;3 private Date birth;4 public Sheep(String name, Date birth) {5 …...
C# TextBox模糊查询及输入提示
在程序中,我们经常会遇到文本框中不知道输入什么内容,这时我们可以在文本框中显示提示词提示用户;或者需要查询某个内容却记不清完整信息,通常可以通过文本框列出与输入词相匹配的信息,帮助用户快速索引信息。 文本框…...
Node入门以及express创建项目
前言 记录学习NodeJS 一、NodeJS是什么? Node.js 是一个开源和跨平台的 JavaScript 运行时环境 二、下载NodeJs 1.下载地址(一直点击next即可,记得修改安装地址) https://nodejs.p2hp.com/download/ 2.查看是否安装成功,打开命令行 nod…...
Cheat Engine CE v7.5 安装教程(专注于游戏的修改器)
前言 Cheat Engine是一款专注于游戏的修改器。它可以用来扫描游戏中的内存,并允许修改它们。它还附带了调试器、反汇编器、汇编器、变速器、作弊器生成、Direct3D操作工具、系统检查工具等。 一、下载地址 下载链接:http://dygod/source 点击搜索&…...
【实例分享】访问后端服务超时,银河麒麟服务器操作系统分析及处理建议
1.服务器环境以及配置 【机型】 处理器: Intel 32核 内存: 128G 整机类型/架构: x86_64虚拟机 【内核版本】 4.19.90-25.22.v2101.kylin.x86_64 【OS镜像版本】 kylin server V10 SP2 【第三方软件】 开阳k8s 2.问题现象描述 …...
Java中和的区别
在Java中,& 和 && 都是逻辑运算符,但它们之间存在一些重要的区别,特别是在它们如何评估其操作数以及它们的性能影响方面。 短路评估(Short-Circuit Evaluation): &&(逻辑…...
深入理解计算机系统 CSAPP 家庭作业6.34
第一步先求(S,E,B,m) 题目说共C32个字节,块大小B为16个字节,那就是分为两组:0,1.然后每组存4个int 每个4字节 CB*E*S .B16 ,直接映射的E就是1,所以S2 m为啥等于7? 通过写出两个数组所有的地址可以得出m7. 得出高速缓存的参数:(S,E,B,m)(2,1,16,7),注意图6-26每个参数的定义…...
[leetcode 141环形链表]双指针解决环形链表
Problem: 141. 环形链表 文章目录 思路Code 思路 首先想到如果链表为空直接返回false 其次想到用双指针,一个一回走一步,另一个一回走两步 如果是环形,总有一个时刻,两指针会指向同一个节点,而且该结点不能为空(空是快指针遍历完单链表了) Code /*** Definition for singly-li…...
【深度学习】Precision、Accuracy的区别,精确率与准确率:深度学习多分类问题中的性能评估详解
在深度学习的多分类问题中,Precision(精确率)和Accuracy(准确率)是两种常用的性能评估指标,它们各自有不同的定义和用途。 Precision(精确率)的中文发音是:pǔ rēi xī…...
DELL服务器插入新磁盘、创建虚拟磁盘、挂载磁盘步骤
文章目录 一、磁盘清理(可选,针对新硬盘是Foreign状态)1、进入VD Mgmt2、清理新硬盘配置 二、创建虚拟磁盘1、进入Device Settings2、创建虚拟磁盘 三、挂载磁盘到系统1、分区磁盘(注意实际磁盘的名称)2、格式化分区3、…...
springboot与flowable(10):网关服务(排他网关)
一、绘制流程图 排他网关用于对流程中的决策建模。当执行到这个网关时,会按照所有出口顺序流定义的顺序对它们进行计算。选择第一个条件为true的顺序流继续流程。例如员工请假时,小于等于3天由组长审批,大于3天由总监审批。流程案例ÿ…...
Web前端网页源代码:深入剖析与实用技巧
Web前端网页源代码:深入剖析与实用技巧 在Web开发的浩瀚领域中,前端网页源代码扮演着至关重要的角色。它不仅是网页的骨架,更是实现各种交互和视觉效果的基石。本文将从四个方面、五个方面、六个方面和七个方面,对Web前端网页源代…...
聊天页面样式
聊天页面样式 代码: <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><link rel"styleshee…...
网站建设没有图片/嘉兴百度seo
一、list转字符串命令:.join(list)其中,引号中是字符之间的分割符,如“,”,“;”,“\t”等等如:list [1, 2, 3, 4, 5].join(list) 结果即为:12345,.join(list) 结果即为:1,2,3,4,5二…...
上海网站建设与设计公司好/申请百度收录网址
一、问题引入 维护老项目,看到下面一个函数: /// <summary>/// 从ViewState中获取某个属性的值。如果该属性不存在,返回空字符串。/// </summary>/// <param name"PropertyName">属性名称</param>/// <…...
织梦cms网站搬家/手机一键优化
3、构建模板示例三(ant整合svnant) 实现ant可以从svn服务器上检出代码要使用svnant jar文件。 从网上下载svnant 包,下载地址: http://subclipse.tigris.org/files/documents/906/49042/svnant-1.3.1.zip 将下载好的svnant 解压将…...
公司网站建设开发/谷歌浏览器网页版
类的属性都是存放在字典中,所以对类或实例的属性进行操作实际上就是对字典的操作。类的属性相关操作如下图:代码块如下:class Door():"门的类"address "浙江省杭州市"def __init__(self,size,color,type):#构造函数"初始化门的…...
企业网站要更新文章吗/推广的渠道和方法有哪些
编辑:ll ASEMI代理MC34PF3001A7EP原装现货NXP车规级MC34PF3001A7EP 型号:MC34PF3001A7EP 品牌:NXP /恩智浦 封装:QFN-48 批号:2023 安装类型:表面贴装型 引脚数量:48 类型:集…...
网站建设注册小程序/5118网站如何使用免费版
转自:https://www.felix021.com/blog/read.php?1587 最长递增子序列,Longest Increasing Subsequence 下面我们简记为 LIS。 排序LCS算法 以及 DP算法就忽略了,这两个太容易理解了。 假设存在一个序列d[1..9] 2 1 5 3 6 4 8 9 7…...