Java实战中如何使用多线程(线程池)及其为什么使用?

这个话题在入行之前就想过很多次,很多8古文或者你搜索的结果都是告诉你什么提高高并发或者是一些很高大上的话,既没有案例也没有什么公式去证明,但是面试中总是被问到,也没有实战经历,所以面试时一问到多线程的东西就无从下手,接下来我就谈谈我的理解。

1.Java中为什么要使用多线程?

多线程:其实是在主线程中再开启一个线程去执行。

为什么要用呢?

先不谈什么高并发,这三个字太荒谬。从基本的作用开始说,其实就是对于一个请求内的耗时操作另外开启一个新的异步线程,不让耗时操作阻塞主线程,这样主线程直接跳过耗时操作去往下面执行,然后返回结果给前端,提高了响应速度。

上案例,这个我们先看一下没有使用多线程的正常请求?

Controller

Service

很简单的东西,我模拟testThread这个里面的东西是一个耗时操作,让它睡了10秒,也就是相当于一个耗时操作。正常请求的话前端肯定是得等10s以后才有响应,就会显示一直加载

正常打印结果

也就是内容是自上而下依次执行的。

上面案例全是靠主线程自己执行的,假如现在使用多线程,就是对于10s耗时的testThread这个操作开启一个异步线程去执行,会有什么效果?此处暂时只加上一个注解就表示这个方法使用了多线程,下面会介绍如何使用,此处先知道就是开启了异步线程。

再来看看响应结果及其速度,

结果表明,它会先打印主线程的东西,然后再打印开启的异步线程内的东西,并且呢可能大家以为主线程提前返回了,那个10s的耗时操作怎么办?结束了吗?其实并没有它只是在后台继续执行10s结束后打印的“66666666666666”,但是我们提前结束了请求响应给了前端。这样前端就不用等很长时间才能拿到结果对吧。别说10s,实际2s都太慢了,但是这个耗时操作一般是什么呢?例如发送邮件,读取文件,等等,也可能是公司的具体业务,并且这个耗时操作的结果不影响你下面继续执行的代码,即你继续执行的代码不依赖于耗时操作的结果。

所以第一个用处就可总结为:开启异步线程处理耗时操作,提高请求的响应速度。

其次再谈谈下一个作用,充分利用资源,提高了CPU的利用率。什么意思呢?我们知道CPU在同一时间内是支持多个线程同时执行的,我们如果不使用多线程,也就是主线程自己去完成所有操作,一定会因为耗时操作阻塞主线程,此时导致CPU空闲,那利用率肯定不高嘛,那我们如果使用异步线程去做,那就是两个线程同时去完成一件事情,多多少少是要比一个线程要快的,而且我们充分利用了它的特性在同一时刻开启多个线程去执行。

所以第二个用户可总结为:分利用资源,提高了CPU的利用率

下面就说说提高高并发怎么回事?上面两个我一开始就知道而且比较好理解,但是高并发这个确实抽象,但是好在下点功夫还是能理解的。高并发:最多能同时处理的请求数量。我们知道那这个肯定是我们服务器的功能,拿我们后端Tomcat来说,支持最大线程数200,最大并发量1000,我就可好奇,200个线程怎么处理1000请求的,很离谱,并且一个请求就耗费一个线程了,实际上是1秒内,有可能一个请求0.5s就结束了,此时线程让出来了处理其他请求了。所以理想情况下在1s内还是有可能的,但是为什么说使用多线程也可以呢,因为这样的话,首先Tomcat的线程可以理解为我们的主线程,如何让主线程更快的释放出来,然后让它去处理别的请求,这才是我们关心的,而我们多线程刚好就解决了这个困难,首先我们自己去创建一个异步线程去处理耗时操作,这个异步线程你想创建多少个都没关系,跟Tomcat提供的那个主线程没关系,不会占用它的那200个。这样我们就让耗时操作让我们的异步线程执行,主线程就释放出来了,这样就提高了整体的并发量。

上面所说的主线程就是main线程,是由Tomcat提供的。而异步线程是jdk提供的。

所以第三可总结为:提高系统的并发处理能力

2.多线程的创建?

1.继承Thread
2.实现Runnabe接口
3.实现Callable接口
4.Executors.newFixedThreadPool

好的上面这个是标准的八股文,但实际工作中不用,1,2,3不用我说也肯定不使用,已经有线程池了,肯定使用线程池的池化思想。好处再帮各位回忆一下

1.减少频繁创建线程和销毁线程带来的额外开销,线程池可以提前帮我们创建好这些线程。

2.避免一直创建线程造成OOM,而线程池会把使用过的线程再使用,避免重复创建线程。
.......还有一些,自己下去可以会议下。
 

那上面我们都不用,但是得知道,因为面试造火箭,这些先表明你都知道,后面紧接着说,实际工作中并不会使用上面的几种方式去创建多线程,而是使用spirng给我们提供的ThreadPoolTaskExecutor或者是jdk提供的ThreadPoolExecutor,一般还是用sping提供的,毕竟现在项目都是spring家族的,IOC这么好用,必须得用上。下面上实战。

3.在代码中如何使用多线程(线程池的方式)?

我就说我在项目中看到的都是使用spring提供的ThreadPoolTaskExecutor,而并没有使用jdk提供的ThreadPoolExecutor,并不是说不可以,肯定是因为spirng提供的更简单,它提供的IOC自动注入,使用相关注解直接使用,比我们自己去创建对象更方便,也更实用。但是一定不要使用Executors.newFixedThreadPool这玩意去创建,阿里开发规范中也明确禁止,为什么禁止?

1. 隐藏关键配置参数:`Executors`提供的便捷方法通常会隐藏线程池的重要配置参数,比如线程池的大小、工作队列类型及容量、拒绝策略等。
    这限制了开发者对线程池行为的精确控制和优化,可能导致资源使用不当或性能问题。
2. 潜在的资源耗尽风险:
   (1)`newFixedThreadPool`和`newSingleThreadExecutor`使用的是无界队列(通常为`LinkedBlockingQueue`),
      这意味着如果生产任务的速度超过消费速度,队列会无限增长,最终可能导致内存耗尽(Out Of Memory Error)。
   (2)`newCachedThreadPool`创建的是一个线程数量无界的线程池,当大量短期异步任务提交时,可能会迅速创建大量线程,消耗过多系统资源

总结一句:缺少参数配置,不可控,可能会造成OOM,消耗过多的系统资源。

如何使用?

1.创建相关的配置类,起个名字,一般叫XxxThreadPoolConfig(Xxx为相关的业务的名称)

@Component
@Configuration
public class TestThreadPoolConfig {@Bean("test_thread_pool") // 设置默认线程名称public Executor ticketing() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setThreadNamePrefix("test_thread_pool-");executor.setMaxPoolSize(80); // 设置最大线程数executor.setCorePoolSize(40);//设置核心线程数executor.setQueueCapacity(100);executor.setKeepAliveSeconds(300);// 设置线程活跃时间(秒)executor.setAllowCoreThreadTimeOut(true);executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 设置拒绝策略executor.setWaitForTasksToCompleteOnShutdown(true); // 等待所有任务结束后再关闭线程池executor.initialize();return executor;}}

2.在启动类上加上开启异步的注解@EnableAsync

@SpringBootApplication
@EnableAsync
public class SystemAdminApplication {public static void main(String[] args) {SpringApplication.run(SystemAdminApplication.class,args);}
}

3.在对应的耗时接口ServiceImpl上直接使用注解@Async("xxx")xxx为配置中@Bean中的名字

 @Async("test_thread_pool")public String testThread() {try {Thread.sleep(10000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("666666666666666");return "hello";}

4.总结

为什么要使用多线程,哪些业务中用到了(实际开发业务中),怎么用的(多线程的创建方式)?

(1)处理耗时操作,提高响应速度,举例
(2)充分利用系统cpu的资源
(3)提高系统的并发量,拿上述的comcat来举例
(4)你怎么使用的?也就是项目如何用的,说我总结的第三点,具体Service业务,如果不知道怎么编,自己上网搜搜,比如发送邮件,读取文件等等,我的话肯定就是说自己项目中遇到的业务。
 

本人还是菜鸟,错误还希望大佬指点。。。。。。。
 

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

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

相关文章

深度学习入门——与学习相关的技巧

前言 本章将介绍神经网络的学习中的一些重要观点,主题涉及寻找最优权重参数的最优化方法、权重参数的初始值、超参数的设定方法等 此外,为了应对过拟合,本章还将介绍权值衰减、Dropout等正则化方法,并进行实现。 最后将对近年来…

细说MCU用单路DAC模块设计和输出锯齿波的实现方法

目录 一、STM32G474RE的DAC模块 二、配置 1.配置DAC 2.选择时钟源和Debug 3.配置系统时钟 三、代码修改 1.启动DAC 2.给DAC的数据输出寄存器赋值 3.运行并观察输出 一、STM32G474RE的DAC模块 有些MCU本身就带有数/模转换器(Digital to Analog Converter,DAC)模块&am…

昇思25天学习打卡营第20天|Diffusion扩散模型

Mindspore框架利用扩散模型DDPM生成高分辨率图像(生成高保真图像项目实践) Mindspore框架利用扩散模型DDPM生成高分辨率图像|(一)关于denoising diffusion probabilistic model (DDPM)模型Mindspore框架利…

差分进化算法原理及其MATLAB/Python代码

1.算法简介 引用自:Storn R, Price K. Differential evolution–a simple and efficient heuristic for global optimization over continuous spaces[J]. Journal of global optimization, 1997, 11: 341-359. 今天给大家带来的是一个非常经典的智能优化算法–差分…

【多模态】42、LLaVA-UHD | 支持任意纵横比和大分辨率图像输入的 LLaVA

论文:LLaVA-UHD: an LMM Perceiving Any Aspect Ratio and High-Resolution Images 代码:https://github.com/thunlp/LLaVA-UHD 出处:清华 | 新加坡国立大学 | 中国科学院大学 一、背景 现有的很多 LMM 都是将图像处理成固定的纵横比&…

Ubuntu编译ffmpeg并添加cmake工程

文章目录 前言前提须知为什么要自己编译 FFmpeg前提软件包与工具的安装编译ffmpeg写CMakeList.txt包含ffmpeg到我们项目中 总结 前言 FFmpeg 是一个领先的多媒体框架,能够解码、编码、转码、复用、解复用、流化、过滤和播放几乎所有人类和机器创造的内容。FFmpeg 包…

设计模式11-原型模式

设计模式11-原型模式 写在前面对象创建模式典型模式原型模式动机结构代码推导应用特点要点总结 原型模式与工厂方法模式对比工厂方法模式原型模式什么时候用什么模式 写在前面 对象创建模式 通过对象创建模式绕开动态内存分配来避免创建过程中所导致的耦合过紧的问题。从而支…

数学建模--国赛备赛---TOPSIS算法

目录 1.准备部分 1.1提交材料 1.2MD5码相关要求 2.TOPSIS算法 2.1算法概述 2.2基本概念 2.3算法核心思想 2.4拓展思考 3.适用赛题 3.1适用赛题说明 3.2适用赛题举例 4.赛题分析 4.1指标的分类 4.2数据预处理 4.2.1区间型属性的变换 4.2.2向量规范化 4.3数据加…

基于 Three.js 的 3D 模型加载优化

作者:来自 vivo 互联网前端团队- Su Ning 作为一个3D的项目,从用户打开页面到最终模型的渲染需要经过多个流程,加载的时间也会比普通的H5项目要更长一些,从而造成大量的用户流失。为了提升首屏加载的转化率,需要尽可能…

IDEA的断点调试(Debug)

《IDEA破解、配置、使用技巧与实战教程》系列文章目录 第一章 IDEA破解与HelloWorld的实战编写 第二章 IDEA的详细设置 第三章 IDEA的工程与模块管理 第四章 IDEA的常见代码模板的使用 第五章 IDEA中常用的快捷键 第六章 IDEA的断点调试(Debug) 第七章 …

【内网安全】横向移动-Kerberos-SPN-WinRM-RDP

目录 环境介绍与横向移动前置域横向移动-WinRM-WinRS移动条件: 步骤0、攻击机开启winrm服务:1.CS探针5985端口:2.连接目标主机并执行命令:3.上线CS&MSF:4.CS内置横向移动-winrm 域横向移动-RDP简介与条件RDP横向移动连接的三种…

TCP状态转换详解

1.什么是TCP的状态转换 TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层协议。在 TCP 连接的生命周期中,连接的状态会随着不同阶段的通信而发生变化,这些变化被称为状…

嵌入式Linux入门知识点汇总-启动过程、设备树、设备框架、锁

目录 1.BootLoader启动过程? 引导加载程序(Bootloader) 补充u-boot的理解 通用的bootloader 2.系统调用过程? 3.设备驱动模型的三个重要成员? 4.驱动和设备注册是否存在先后顺序? 5.framebuffer机制? 6.字符设备和块设备的区别并分别举例? 1.字符设备 2.块设备…

SVN 服务 安装部署 Docker(compose) 方式

通过 dockerhub 或者 命令行运行 : docker search svn 查看 svn 的镜像 如命令行: [rootSGP ~]# docker search svn NAME DESCRIPTION STARS OFFICIAL AUTOMATED garethflower…

景联文科技构建高质量心理学系知识图谱,助力大模型成为心理学科专家

心理大模型正处于快速发展阶段,在临床应用、教育、研究等多个领域展现出巨大潜力。 心理学系知识图谱能够丰富心理大模型的认知能力,使其在处理心理学相关问题时更加精确、可靠和有洞察力。这对于提高心理健康服务的质量和效率、促进科学研究以及优化教育…

【Django】网上蛋糕商城后台-订单管理

概念 前面通过多篇文章以完全实现了用户在网上蛋糕商城平台上的所有功能和操作,从本文开始,实现网站的后台管理功能的介绍和操作。 导入静态资源 在static文件夹下,创建admin文件夹,在该文件夹下导入静态资源 在templates文件夹…

cs224w笔记(p5)

链接预测任务的两种类型:随机缺失边;随时间演化边。 第一种假设可以以蛋白质之间的交互作用举例,缺失的是研究者还没有发现的交互作用。 第二种假设可以以社交网络举例,随着时间流转,人们认识更多朋友。 基于相似性进…

zookeeper+kafka消息队列集群部署

一.消息队列 1、什么是消息队列 消息(Message)是指在应用间传送的数据。消息可以非常简单,比如只包含文本字符串,也可以更复杂,可能包含嵌入对象。 消息队列(MessageQueue)是一种在软件系统中用…

1、springboot3 vue3开发平台-后端-项目构建

文章目录 1. 创建项目1.1 前置环境条件1.2 项目创建 2. 模块配置2.1 父工程配置概述2.2 配置启动模块2.3 父工程相关依赖管理 1. 创建项目 1.1 前置环境条件 idea2023, jdk17 1.2 项目创建 创建父工程并删除不需要的文件目录: 右键父工程依次创建其他模块 最…

Windows 、Linux、MacOS 进程管理机制

本心、输入输出、结果 文章目录 Windows 、Linux、MacOS 进程管理机制前言Windows 进程管理机制Linux 进程管理macOS 进程管理内存不够了,几个操作系统如何处理Windows 、Linux、MacOS 进程管理机制 编辑 | 简简单单 Online zuozuo 地址 | https://blog.csdn.net/qq_15071263 …