为什么要用线程池?

线程池是一种管理和复用线程资源的机制,它由一个线程池管理器和一组工作线程组成。线程池管理器负责创建和销毁线程池,以及管理线程池中的工作线程。工作线程则负责执行具体的任务。

线程池的主要作用是管理和复用线程资源,避免了线程的频繁创建和销毁所带来的开销。
线程池包含两个重要的组成部分:

  1. 线程池大小:指线程池中所能容纳的最大线程数。线程池大小一般根据系统的负载情况和硬件资源来设置。

  2. 工作队列:用于存放等待执行的任务。当线程池中的工作线程已经全部被占用时,新的任务将被加入到工作队列中等待执行。

线程池创建方式

Java中线程池的创建方式主要有以下几种:

  1. 使用 ThreadPoolExecutor 类手动创建:通过 ThreadPoolExecutor 类的构造函数自定义线程池的参数,包括核心线程数、最大线程数、线程存活时间、任务队列等。

  2. 使用 Executors 类提供的工厂方法创建:通过 Executors 类提供的一些静态工厂方法创建线程池,例如 newFixedThreadPool、newSingleThreadExecutor、newCachedThreadPool 等。

  3. 使用 Spring 框架提供的 ThreadPoolTaskExecutor 类:在 Spring 框架中可以通过 ThreadPoolTaskExecutor 类来创建线程池。

不同的创建方式适用于不同的场景,通常可以根据实际情况选择合适的方式创建线程池。手动创建 ThreadPoolExecutor 类可以灵活地配置线程池参数,但需要对线程池的各项参数有一定的了解;使用 Executors 工厂方法可以快速创建线程池,但可能无法满足特定的需求,且容易出现内存溢出的情况;而 Spring 框架提供的 ThreadPoolTaskExecutor 类则只能在 Spring 框架中使用。

线程池使用

ThreadPoolExecutor 使用

ThreadPoolExecutor 线程的创建与使用示例如下:

import java.util.concurrent.*;public class ThreadPoolDemo {public static void main(String[] args) {// 创建线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(2, // 核心线程数5, // 最大线程数60, // 线程池中线程的空闲时间(单位为秒)TimeUnit.SECONDS, // 时间单位new ArrayBlockingQueue<>(10) // 任务队列);// 提交任务for (int i = 1; i <= 20; i++) {final int taskId = i;executor.submit(() -> {System.out.println("执行任务:" + taskId + ",线程名称:" + Thread.currentThread().getName());try {Thread.sleep(1000); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}});}// 关闭线程池executor.shutdown();}
}

在上面的示例中,我们通过 ThreadPoolExecutor 类手动创建了一个线程池,设置了线程池的核心线程数为 2,最大线程数为 5,线程空闲时间为 60 秒,任务队列为长度为 10 的 ArrayBlockingQueue。然后我们通过 submit() 方法向线程池中提交了 20 个任务,每个任务会在执行时输出自己的编号和执行线程的名称,然后睡眠1秒钟模拟任务执行时间。最后,我们调用 shutdown() 方法关闭线程池。

Executors 使用

import java.util.concurrent.*;public class ExecutorsDemo {public static void main(String[] args) {// 创建线程池ExecutorService executor = Executors.newFixedThreadPool(5);// 提交任务for (int i = 1; i <= 10; i++) {final int taskId = i;executor.submit(() -> {System.out.println("执行任务:" + taskId + ",线程名称:" + Thread.currentThread().getName());try {Thread.sleep(1000); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}});}// 关闭线程池executor.shutdown();}
}

在上面的示例中,我们使用了 Executors 工厂类中的 newFixedThreadPool() 方法创建了一个线程池,该线程池有 5 个固定线程。然后我们通过 submit() 方法向线程池中提交了 10 个任务,每个任务会在执行时输出自己的编号和执行线程的名称,然后睡眠 1 秒钟模拟任务执行时间。最后,我们调用 shutdown() 方法关闭线程池。值得注意的是,使用 Executors 工厂类创建线程池虽然非常简单,但是在实际生产环境中并不推荐,因为这种方式很容易导致线程资源被耗尽,从而影响系统的性能和稳定性。

ThreadPoolTaskExecutor 使用

Spring 中 ThreadPoolTaskExecutor 的使用示例如下:

import java.util.concurrent.*;import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;public class ThreadPoolTaskExecutorDemo {public static void main(String[] args) {// 创建线程池ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(2); // 核心线程数executor.setMaxPoolSize(5); // 最大线程数executor.setQueueCapacity(10); // 任务队列长度executor.initialize();// 提交任务for (int i = 1; i <= 20; i++) {final int taskId = i;executor.submit(() -> {System.out.println("执行任务:" + taskId + ",线程名称:" + Thread.currentThread().getName());try {Thread.sleep(1000); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}});}// 关闭线程池executor.shutdown();}
}

在上面的示例中,我们使用了 Spring 框架中的 ThreadPoolTaskExecutor 类创建了一个线程池,设置了线程池的核心线程数为 2,最大线程数为 5,任务队列长度为 10。然后我们通过 submit() 方法向线程池中提交了 20 个任务,每个任务会在执行时输出自己的编号和执行线程的名称,然后睡眠1秒钟模拟任务执行时间。最后,我们调用 shutdown() 方法关闭线程池。注意,在使用 Spring 框架中的 ThreadPoolTaskExecutor 类创建线程池时,我们需要先调用 initialize() 方法进行初始化。

ThreadPoolTaskExecutor 底层还是通过 JDK 中提供的 ThreadPoolExecutor 类实现的。

线程池优点详解

线程池相比于线程来说,它不需要频繁的创建和销毁线程,线程一旦创建之后,默认情况下就会一直保持在线程池中,等到有任务来了,再用这些已有的线程来执行任务,如下图所示:

image.png

优点1:复用线程,降低资源消耗

线程在创建时要开辟虚拟机栈、本地方法栈、程序计数器等私有线程的内存空间,而销毁时又要回收这些私有空间资源,如下图所示:
 

image.png


而线程池创建了线程之后就会放在线程池中,因此线程池相比于线程来说,第一个优点就是可以复用线程、减低系统资源的消耗

优点2:提高响应速度

线程池是复用已有线程来执行任务的,而线程是在有任务时才新建的,所以相比于线程来说,线程池能够更快的响应任务和执行任务。

优点3:管控线程数和任务数

线程池提供了更多的管理功能,这里管理功能主要体现在以下两个方面:

  1. 控制最大并发数:线程池可以创建固定的线程数,从而避免了无限创建线程的问题。当线程创建过多时,会导致系统执行变慢,因为 CPU 核数是一定的、能同时处理的任务数也是一定的,而线程过多时就会造成线程恶意争抢和线程频繁切换的问题,从而导致程序执行变慢,所以合适的线程数才是高性能运行的关键。
  2. 控制任务最大数:如果任务无限多,而内存又不足的情况下,就会导致程序执行报错,而线程池可以控制最大任务数,当任务超过一定数量之后,就会采用拒绝策略来处理多出的任务,从而保证了系统可以健康的运行。

优点4:更多增强功能

线程池相比于线程来说提供了更多的功能,比如定时执行和周期执行等功能。

小结 

线程池是一种管理和复用线程资源的机制。相比于线程,它具备四个主要优势:1.复用线程,降低了资源消耗;2.提高响应速度;3.提供了管理线程数和任务数的能力;4.更多增强功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/88788.shtml

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

相关文章

星河AI网络,大模型纪元的运力答案

随着AI预训练大模型的价值不断显现&#xff0c;且模型规模愈发庞大。产学各界已经形成了这样一个共识&#xff1a;AI时代&#xff0c;算力就是生产力。 这一认知虽然正确&#xff0c;却并不全面。数字化系统有存、算、网三大支柱&#xff0c;AI技术也是如此。如果抛开存储和网络…

diskGenius专业版使用:windows系统下加载ext4 linux系统分区并备份还原资源(文件的拷贝进、出)

前言 EXT4是第四代扩展文件系统&#xff08;英语&#xff1a;Fourth extended filesystem&#xff0c;缩写为 ext4&#xff09;是Linux系统下的日志文件系统&#xff0c;是ext3文件系统的后继版本。 所以我们在windows系统下是不能识别的&#xff0c;也不能对其写入、拷贝出文…

Azure AD混合部署,通过 Intune 管理设备,实现条件访问

需求&#xff1a; 公司要求&#xff0c;非公司设备不允许使用 邮箱&#xff0c;Teams等O365服务。 我们可以通过 Intune 中的 "条件访问" 解决这个问题。 一、设备同步到 AAD 1、配置 AAD Connect 2、选择 3、下一步 4、配置本地 企业管理员 5、配置成功 二、设备…

DataExcel控件读取和保存excel xlsx 格式文件

需要引用NPOI库 https://github.com/dotnetcore/NPOI 调用Read 函数将excel读取到dataexcel控件 调用Save 函数将dataexcel控件文件保存为excel文件 using NPOI.HSSF.UserModel; using NPOI.HSSF.Util; using NPOI.SS.UserModel; using NPOI.SS.Util; using System; using …

torch.sum()——dim参数

dim指在dim的这个维度上&#xff0c;对tesnor 进行求和&#xff0c;如果keepdim&#xff08;保持维度&#xff09;False&#xff0c;返回结果会删去dim所指的这个维度。以下面的例子分析dim的参数~ torch.tensor([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]) print(…

【C++STL基础入门】list交换、翻转,排序、合并和拼接操作

文章目录 前言一、交换list二、翻转list三、排序list四、合并list五、拼接list总结 前言 在C的标准模板库&#xff08;STL&#xff09;中&#xff0c;list是一个双向链表容器&#xff0c;提供了丰富的功能和操作。本文将介绍list容器在交换、翻转、排序、合并和拼接等方面的基…

消息中间件

rabbitmq如何保证消息不丢失&#xff1f; 先要看生产者发送消息再哪个环节会丢失&#xff1f; 1 生产者发送消息到交换机失败&#xff08;生产者服务宕机&#xff09; 2 交换机到队列失败 3队列中消息丢失 消息失败后怎么处理呢&#xff1f; 可以设置回调方法重发 记录日…

【JVM】运行时数据区之方法区——自问自答

开局从康师傅那里借图几张 线程共享与私有 《Java虚拟机规范》中明确说明:“尽管所有的方法区在逻辑上是属于堆的一部分&#xff0c;但一些简单的实现可能不会选择去进行垃圾收集或者进行压缩。” 但对于HotSpotJVM而言&#xff0c;方法区还有一个别名叫做Non-Heap(非堆)&#…

Vue之ElementUI之动态树+数据表格+分页(项目功能)

目录 前言 一、实现动态树形菜单 1. 配置相应路径 2. 创建组件 3. 配置组件与路由的关系 index.js 4. 编写动态树形菜单 5. 页面效果演示 二、实现数据表格绑定及分页功能 1. 配置相应路径 2. 编写数据表格显示及分页功能代码 BookList.vue 3. 演示效果 总结 前言…

vue 普通组件的 局部注册

vue 普通组件的 注册 11 Vue2_3入门到实战-配套资料\01-随堂代码素材\day03\素材\00-准备代码\小兔鲜首页静态页\src

蓝桥杯每日一题20223.9.26

4407. 扫雷 - AcWing题库 题目描述 分析 此题目使用map等都会超时&#xff0c;所以我们可以巧妙的使用哈希模拟散列表&#xff0c;哈希表初始化为-1首先将地雷读入哈希表&#xff0c;找到地雷的坐标在哈希表中对应的下标&#xff0c;如果没有则此地雷的位置第一次出现&#…

MySQL基础篇-约束

目录 1.约束概述 2.分类 3.测试user表的约束情况 主键约束 非空约束及唯一约束 检查约束 默认约束 4.外键约束 外键约束的语法 外键约束的删除/更新行为 小结 1.约束概述 MySQL约束&#xff08;Constraints&#xff09;是用于确保表中数据完整性和一致性的规则。它们定…

vue3 + mark.js | 实现文字标注功能

页面效果 具体实现 新增 1、监听鼠标抬起事件&#xff0c;通过window.getSelection()方法获取鼠标用户选择的文本范围或光标的当前位置。2、通过 选中的文字长度是否大于0或window.getSelection().isCollapsed (返回一个布尔值用于描述选区的起始点和终止点是否位于一个位置&…

2.(vue3.x+vite)组件注册并调用

前端技术社区总目录(订阅之前请先查看该博客) 关联博客 1.(vue3.x+vite)封装组件 一:umd调用方式 1:引入umd.js <script src="./public/myvue5.umd.js"></script>2:编写代码调用 (1)umd方式,根据“5

爬虫 — 多线程

目录 一、多任务概念二、实现多任务方式1、多进程 &#xff08;Multiprocessing&#xff09;2、多线程&#xff08;Multithreading&#xff09;3、协程&#xff08;Coroutine&#xff09; 三、多线程执行顺序四、多线程的方法1、join()2、setDaemon()3、threading.enumerate() …

Vue+ElementUI实现动态树和表格数据的分页模糊查询

目录 前言 一、动态树的实现 1.数据表 2.编写后端controller层 3.定义前端发送请求路径 4.前端左侧动态树的编写 4.1.发送请求获取数据 4.2.遍历左侧菜单 5.实现左侧菜单点击展示右边内容 5.1.定义组件 5.2.定义组件与路由的对应关系 5.3.渲染组件内容 5.4.通过动态…

Denoising diffusion implicit models 阅读笔记

Denoising diffusion probabilistic models (DDPMs)从马尔科夫链中采样生成样本&#xff0c;需要迭代多次&#xff0c;速度较慢。Denoising diffusion implicit models (DDIMs)的提出是为了加速采样过程&#xff0c;减少迭代的次数&#xff0c;并且要求DDIM可以复用DDPM训练的网…

lv5 嵌入式开发-7 有名管道和无名管道

目录 1 进程间通信介绍 2 无名管道 2.1 无名管道特点 ​编辑 2.2 读无名管道 2.3 写无名管道 3 有名管道 3.1 有名管道特点 3.2 写有名管道 3.3 读有名管道 掌握&#xff1a;进程间通信方式介绍、无名管道特点、无名管道创建、无名管道读写特性&#xff1b;有名管道…

深入浅出DAX:SELECTEDVALUE()

深入浅出DAX&#xff1a;SELECTEDVALUE() SELECTEDVALUE()&#xff0c;如果筛选 columnName 的上下文后仅剩下一个非重复值&#xff0c;则返回该值。否则返回alternateResult&#xff0c;语法如下&#xff1a; SELECTEDVALUE(<columnName>[, <alternateResult>] …

Rabbit消息的可靠性

生产者重连 消费者重试 Confirm模式简介 消息的confirm确认机制&#xff0c;是指生产者投递消息后&#xff0c;到达了消息服务器Broker里面的exchange交换机&#xff0c;则会给生产者一个应答&#xff0c;生产者接收到应答&#xff0c;用来确定这条消息是否正常的发送到Broker…