多线程学习-线程池
目录
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…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
文章目录 前言第一部分:体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分:体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...
Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...
[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...
