多线程学习-线程池
目录
1.线程池的作用
2.线程池的实现
3.自定义创建线程池
1.线程池的作用
当我们使用Thread的实现类来创建线程并调用start运行线程时,这个线程只会使用一次并且执行的任务是固定的,等run方法中的代码执行完之后这个线程就会变成垃圾等待被回收掉。如果是使用实现Runnable接口或者使用实现Callable接口先创建一个任务类,再将任务传递给创建的线程,那这个线程虽然可以用来执行不同的任务,只需要将不同的任务类对象传递给这个线程即可,但仍旧无法解决线程使用的一次性问题。我们希望一个线程不仅是能够通用的,而且还是能够复用的,这就需要线程池来帮忙。
线程池顾名思义就是一个存放线程的池子,当执行一个任务需要线程时就从里面取出一个,用完之后再还回线程池。其特点是:
- 初始状态下线程池是空的,里面没有线程,需要线程时才会创建线程。
- 当线程池创建的线程数还没有达到线程池的容量时,如果有任务需要一个线程但线程池中没有空闲线程,线程池会重新创建一个线程放到线程池中。
- 当线程池创建的线程数达到线程池的容量时,即使线程池中没有空闲线程也不会继续创建,这时那些需要线程的任务会进入一个等待队列,等到其他任务归还线程后才能根据先到先得的原则获取线程。
- 当任务执行完毕后所使用的线程会归还给线程池,此时这个线程又变成空闲线程。
2.线程池的实现
线程池有两种,一种是无限线程池,理论上线程池的容量是无限的,实际上其最大容量为int的最大范围,但由于实际生活中创建一个int范围的线程基本上不能实现,并且也无法同时运行这么多的线程,所以可以说是无限的;另一种则是有限线程池,在创建时需要指定容量。

代码实现:
//实现Runnable接口创建的任务类
public class MyRun implements Runnable{@Overridepublic void run() {for(int i=0;i<100;i++)System.out.println(Thread.currentThread().getName()+"正在执行MyRun的任务,输出"+i);}
}//实现Callable接口创建的任务类
public class MyCall implements Callable<String> {@Overridepublic String call() throws Exception {return Thread.currentThread().getName()+"正在执行MyCall的任务";}
}//使用线程池
public class Main {public static void main(String[] args) throws ExecutionException, InterruptedException {//无限线程池的创建ExecutorService pool= Executors.newCachedThreadPool();//有限线程池的创建ExecutorService pool1=Executors.newFixedThreadPool(3);//创建两种类型的任务类Runnable r=new MyRun();Callable<String> c=new MyCall();FutureTask<String> ft=new FutureTask<String>(c);//提交任务,提交之后就会向线程池申请线程并执行任务pool1.submit(r);//由于Callable类型的任务的返回值需要FutureTask管理,所以提交的是ftpool1.submit(ft);System.out.println(ft.get());//销毁线程池pool1.shutdown();pool.shutdown();}
}
运行结果:


这里可以发现线程池创建的线程是以“pool-线程池编号-thread-线程编号”来命名的。线程池和所创建的线程的编号都是从1开始,可以查看源码:


在上面的例子中,有限线程池设置的容量为3,但只提交了两个任务,所以不会出现有任务获取不到线程而进入等待队列的情况。
下面测试进入等待队列的情况,提交4个Runnable类型的任务,因为一个任务要循环100次输出耗时较长,可以在提交第四个任务时保证线程池中没有空闲线程,然后让其进入等待队列。利用Debug来查看是否有任务进入了等待队列,先准备好测试代码并加上断点:

开始调试,线程池在创建后可以看到它的属性:
下面执行提交第一个任务:

提交第二个任务:

提交第三个任务:

提交第四个任务:

补充:在实际情况下服务器通常都是一直工作,所以线程池会一直被使用,就没有必要销毁掉,所以一般情况下不会用到销毁操作。
3.自定义创建线程池
一个线程池所创建的线程分为核心线程和临时线程,核心线程则是在创建后会一直存在,直到线程池关闭,而临时线程则是当提交的任务过多时应急使用的,临时线程也会正常工作,但在工作结束后如果在一定时间内没有其他任务,则会被销毁。要注意的是,只有当等待队列的任务占满了整个队列,后面再提交任务时才会创建临时线程,并且创建的临时线程处理的任务并不是等待队列中的任务,而是队列满后后面再提交的任务。如果创建的临时线程达到最大数量,此时仍有任务被提交时就需要选择一种应对策略来处理后面提交的任务。
自定义线程池需要设置以下七种参数:
- 允许创建的最大核心线程数量(不能小于0)
- 允许创建的最大线程数量(不能小于0且必须>=核心线程数量,最大线程数量减去最大核心线程数量就是可以创建的最大临时线程数量)
- 临时线程的最大空闲时间1(设置时间的值,不能小于0)
- 临时线程的最大空闲时间2(设置时间的单位,使用TimeUnit指定)
- 等待队列(其实就是一个阻塞队列,不能为null)
- 创建线程的工厂(也就是怎样创建一个线程,不能为null)
- 应对策略(一共有四种策略,四选一,一般选择第一个,不能为null)

自定义创建线程池代码实现:
//创建自定义线程池
ThreadPoolExecutor pool2=new ThreadPoolExecutor(3, //设置核心线程的最大创建数量5, //设置最大线程数量(最大临时线程数量也就是5-3=2)60, //设置临时线程的最大空闲时间的值部分TimeUnit.SECONDS, //设置临时线程的最大空闲时间的单位部分new ArrayBlockingQueue<>(5), //加入阻塞队列作为等待队列Executors.defaultThreadFactory(), //使用java提供的线程池默认创建线程的工厂作为创建线程的工厂即可new ThreadPoolExecutor.AbortPolicy() //选择第一种应对策略,AbortPolicy是一个内部类);
使用方式和普通线程一样,提交任务用submit,销毁用shutdown。
补充内部类:内部类是在一个类中定义另一个类。当这个类需要依附于另一个类但这个类本身也是独立的一部分时可以将这个类创建为其他类的内部类,就比如发动机和汽车,发动机需要依附于汽车才能发挥作用,但发动机本身又是独立的一部分。
那线程池的最大线程数量是不是越大越好呢?其实不然,线程池的最大线程数量通常是按照规定来的,这取决于开发的项目是CPU密集型还是I/O密集型的。
CPU密集型也就是所开发的项目需要进行的运算偏多,而执行运算操作就要用到CPU。这种项目所需的最大线程数量应当设置为:最大并行数+1。
所谓最大并行数就是看CPU能最多能分给java多少线程,通常说CPU是多少核多少线程的,核数就是这个CPU有多少大脑,多少线程就是这个CPU有多少只手,每只手对应一个线程,但不一定所有的线程都可以让java调度,我们可以通过下面的代码查看可以分给java的最大线程数:
//查看CPU能分给java的最大线程数
int num=Runtime.getRuntime().availableProcessors();
System.out.println(num);
可以分配给java的最大线程数就是最大并行数,至于要加1是为了当已经创建了的某个线程出现问题时可以利用这个多出来的线程继续工作, 尽可能地将CPU利用率最大化。
I/O密集型就是开发的项目中I/O操作比较多,现在大多数项目都是I/O密集型的。这种情况下线程池的最大线程数量可以设置为:最大并行数*期望CPU的利用率*(总的运行时间/CPU的运行时间)。通常情况下我们希望CPU的利用率越高越好,所以可以设置为100%,最大并行数在CPU密集型部分介绍过了,那什么是CPU的运行时间呢?
比如要执行读取文件中的两个整数并相加的操作,这个操作分为两部分,从文件中读取数据的部分没有用到CPU,而后面的相加部分属于运算,就需要CPU了。所以在这个例子中,CPU的运行时间就是后面相加所需的时间。在实际项目中可以使用thread dump工具来测总的运行时间和CPU的运行时间。
相关文章:
多线程学习-线程池
目录 1.线程池的作用 2.线程池的实现 3.自定义创建线程池 1.线程池的作用 当我们使用Thread的实现类来创建线程并调用start运行线程时,这个线程只会使用一次并且执行的任务是固定的,等run方法中的代码执行完之后这个线程就会变成垃圾等待被回收掉。如…...
Linux第4课 Linux的基本操作
文章目录 Linux第4课 Linux的基本操作一、图形界面介绍二、终端界面介绍 Linux第4课 Linux的基本操作 一、图形界面介绍 本节以Ubuntu系统的GUI为例进行说明,Linux其他版本可自行网搜。 图形系统进入后,左侧黄框内为菜单栏,右侧为桌面&…...
堆排序解读
在算法世界中,排序算法一直是一个热门话题。推排序(Heap Sort)作为一种基于堆这种数据结构的有效排序方法,因其时间复杂度稳定且空间复杂度低而备受青睐。本文将深入探讨推排序的原理、实现方式,以及它在实际应用中的价…...
docker + miniconda + python 环境安装与迁移(详细版)
本文主要列出从安装dockerpython环境到迁移环境的整体步骤。windows与linux之间进行测试。 简化版可以参考:docker miniconda python 环境安装与迁移(简化版)-CSDN博客 目录 一、docker 安装和测试 二、docker中拉取minicondaÿ…...
蓝桥杯刷题第八天(dp专题)
这道题有点像小学奥数题,解题的关键主要是: 有2种走法固走到第i级阶梯,可以通过计算走到第i-1级和第i-2级的走法和,可以初始化走到第1级楼梯和走到第2级楼梯。分别为f[1]1;f[2]1(11)1(2)2.然后就可以循环遍历到后面的状态。 f[i…...
【WEEK6】 【DAY1】DQL查询数据-第一部分【中文版】
2024.4.1 Monday 目录 4.DQL查询数据(重点!)4.1.Data Query Language查询数据语言4.2.SELECT4.2.1.语法4.2.2.实践4.2.2.1.查询字段 SELECT 字段/* FROM 表查询全部的某某查询指定字段 4.2.2.2.给查询结果或者查询的这个表起别名(…...
Linux:权限篇
文章目录 前言1.用户2.文件的权限管理2.1 修改文件的权限2.2 修改文件的拥有者2.3 修改文件的所属组 3.file指令4.umask指令4.目录的权限管理总结 前言 Linux权限在两个地方有所体现,一种是使用用户:分为root超级用户员与普通用户。另一个是体现在文件的…...
Lua热更新(xlua)
发现错误时检查是否:冒号调用 只需要导入asset文件夹下的Plugins和Xlua这两个文件即可,别的不用导入 生成代码 和清空代码 C#调用lua using Xlua; 需要引入命名空间 解析器里面执行lua语法 lua解析器 LuaEnv 单引号是为了避免引号冲突 第二个参数是报错时显示什么提示…...
并查集(基础+带权以及可撤销并查集后期更新)
并查集 并查集是一种图形数据结构,用于存储图中结点的连通关系。 每个结点有一个父亲,可以理解为“一只伸出去的手”,会指向另一个点,初始时指向自己。一个点的根节点是该点的父亲的父亲的..的父亲,直到某个点的父亲…...
基于 Java 的数据结构和算法 (不定期更新)
JavaIsBestLang 数据结构 Collection 是 Java 中的接口,被多个泛型容器接口所实现。在这里,Collection 是指代存放对象类型的数据结构。 ArrayList 函数名功能size()返回 this 的长度add(Integer val)在 this 尾部插入一个元素add(int idx, Integer …...
考研回忆录【二本->211】
备考时长差不多快一年半,从22年的11月底开始陆陆续续地准备考研,因为开始的早所以整个备考过程显得压力不是很大,中途还去一些地方旅游,我不喜欢把自己绷得太紧。虽然考的不是很好,考完我甚至都没准备复试,…...
【XCPC笔记】2023 (ICPC) Jiangxi Provincial Contest——ABCIJKL 做题记录
补题 赛后gym练习及补题,gym链接:2023 (ICPC) Jiangxi Provincial Contest – Official Contest 另外,D题我也打算找机会学习写下,C题的博弈论还需要好好理解,感觉都是比较有趣的数学问题 补题顺序如下 补题L [Zhang …...
猫头虎分享已解决Bug || **URLError (URL错误)** 全方位解析
博主猫头虎的技术世界 🌟 欢迎来到猫头虎的博客 — 探索技术的无限可能! 专栏链接: 🔗 精选专栏: 《面试题大全》 — 面试准备的宝典!《IDEA开发秘籍》 — 提升你的IDEA技能!《100天精通鸿蒙》 …...
如何使用极狐GitLab 启用自动备份功能
本文作者:徐晓伟 GitLab 是一个全球知名的一体化 DevOps 平台,很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版,专门为中国程序员服务。可以一键式部署极狐GitLab。 本文主要讲述了如何极狐GitLab 自…...
HTML/XML转义字符对照
特殊字符转义表 字符十进制转义字符""&&<<<>>>不断开空格(non-breaking space) 最常用的转义字符列表 显示说明实体名称十进制编号半方大的空白 全方大的空白 不断行的空白格 <小于<<>大于&g…...
设计模式:组合模式示例
组合模式的典型例子通常涉及到树形结构的处理,下面是几个形象且易于理解的例子: 文件系统 在文件系统中,目录可以包含文件或者其他目录,但是从用户的角度来看,目录和文件都可以被“打开”或者“获取大小”。这里的目…...
普通情况和高并发时,Redis缓存和数据库怎么保持一致?
普通情况和高并发时,Redis缓存和数据库怎么保持一致? 普通情况思路 高并发时思路 Q:缓存和数据库怎么保持一致? A:绝对不可能保持一致的,在实际业务开发中,有一些方案可以做取舍。 实际业务中&a…...
Django -- 自动化测试
概述 测试是一种例行的、不可缺失的工作,用于检查你的程序是否符合预期。 测试可以划分为不同的级别。一些测试可能专注于小细节(比如某一个模型的方法是否会返回预期的值?), 一些测试则专注于检查软件的整体运行是否…...
NodeJS 在Windows / Mac 上实现多版本控制
NodeJS 的多版本控制 本文介绍一下在 windows/MacOS 上 如何 切换和使用多个版本的 NodeJS。 Windows 本小节介绍一下在windows上管理不同版本的NodeJS。 nvm-windows 工具 nvm-windows 是在 windows 上管理 NodeJS 版本的一个工具。 它可以很方便的 下载、移除、查看、切…...
Web3 游戏周报(3.24-3.30)
【3.24-3.30】Web3 游戏行业动态: Web3 开发平台 Mirror World 在 Solana 上推出首个游戏 rollup 链 NFT 卡牌游戏 Parallel 完成 3,500 万美元融资,Solana Ventures 等参投 加密游戏开发公司 Gunzilla Games 完成 3,000 万美元融资 Telegram 游戏 No…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...
【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...
Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案
在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...
图解JavaScript原型:原型链及其分析 | JavaScript图解
忽略该图的细节(如内存地址值没有用二进制) 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么:保存在堆中一块区域,同时在栈中有一块区域保存其在堆中的地址(也就是我们通常说的该变量指向谁&…...
