spring多线程实现+合理设置最大线程数和核心线程数

1.最简单的方法:

  • 需要在 Spring Boot 主类上添加 @EnableAsync 注解启用异步功能;
  • 需要在异步方法上添加 @Async 注解。

示例代码如下:

@SpringBootApplication
@EnableAsync
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}@Service
public class AsyncService {@Asyncpublic void asyncTask() {// 异步任务执行的逻辑}
}

优点:

  • 简单易用,只需要在方法上添加@Async注解即可。
  • 依赖Spring框架,集成度高,可以与其他Spring组件无缝协作。

缺点:

  • 方法必须是public,否则异步执行无效。
  • 不能直接获取异步执行结果,需要使用Future或CompletableFuture等类型。

 2.基于@Async注解,自己重写线程池配置的方法

2.1、创建线程池
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;@EnableAsync
@Configuration
public class ExecutorConfig {// 获取服务器的 cpu 个数private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();@Bean("taskExecutor")public Executor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 核心线程数executor.setCorePoolSize(CPU_COUNT * 2);// 最大线程数executor.setMaxPoolSize(CPU_COUNT * 4);// 线程空闲保持时间executor.setKeepAliveSeconds(50);// 队列大小executor.setQueueCapacity(CPU_COUNT * 16);// 线程名前缀executor.setThreadNamePrefix("");// 设置拒绝策略// AbortPolicy 	          ->    默认策略,如果线程池队列满了丢掉这个任务并且抛出 RejectedExecutionException 异常// DiscardPolicy          ->    如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常// DiscardOldestPolicy    ->	如果队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列// CallerRunsPolicy       ->	如果添加到线程池失败,那么主线程会自己去执行该任务,不会等待线程池中的线程去执行executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());// 等待所有任务结束后再关闭线程池executor.setWaitForTasksToCompleteOnShutdown(true);executor.initialize();return executor;}
}
2.2、业务方法添加 @Async 注解
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;@Service
public class TestServiceImpl {public static volatile AtomicInteger i = new AtomicInteger(0);/*** 无返回结果*/@Async("taskExecutor")public void test() {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("当前第" + i.incrementAndGet() + "次执行");}/*** 有返回结果*/@Async("taskExecutor")public Future<String> test2() {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}return new AsyncResult("当前第" + i.incrementAndGet() + "次执行");}
}

也可以不用注解,代码如下

package com.socketio.push.config;import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;import javax.annotation.Resource;/*** @author litong*/
@Service
public class RedisSub implements MessageListener {@Resource(name = "taskExecutor")private ThreadPoolTaskExecutor taskExecutor;@Overridepublic void onMessage(Message message, byte[] pattern) {taskExecutor.execute(()->{handleMsg(message);});}/*** 处理redis订阅消息* @param message*/private void handleMsg(Message message){}}
注解方式失效原因

1. 注解的方法必须是 public 方法
2. 方法一定要从另一个类中调用,也就是从类的外部调用,类的内部调用是无效的。
   因为 @Async 注解的实现都是基于 AOP 动态代理,所以内部调用失效的原因是由于调用方法的是对象本身而不是代理对象,没有经过 Spring 容器
3. 异步方法使用注解 @Async 的返回值只能为 void 或者 Future

  3.使用 CompletableFuture 实现异步任务

CompletableFuture 是 Java 8 新增的一个异步编程工具,它可以方便地实现异步任务。使用 CompletableFuture 需要满足以下条件:

  • 异步任务的返回值类型必须是 CompletableFuture 类型;
  • 在异步任务中使用 CompletableFuture.supplyAsync() 或 CompletableFuture.runAsync() 方法来创建异步任务;
  • 在主线程中使用 CompletableFuture.get() 方法获取异步任务的返回结果。

示例代码如下:

@Service
public class AsyncService {public CompletableFuture<String> asyncTask() {return CompletableFuture.supplyAsync(() -> {// 异步任务执行的逻辑return "异步任务执行完成";});}
}

4.使用 TaskExecutor 实现异步任务

TaskExecutor 是 Spring 提供的一个接口,它定义了一个方法 execute(),用来执行异步任务。使用 TaskExecutor 需要满足以下条件:

  • 需要在 Spring 配置文件中配置一个 TaskExecutor 实例;
  • 在异步方法中调用 TaskExecutor 实例的 execute() 方法来执行异步任务。

示例代码如下: 

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {@Bean(name = "asyncExecutor")public TaskExecutor asyncExecutor() {// 获取服务器的 cpu 个数private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(CPU_COUNT);executor.setMaxPoolSize(CPU_COUNT * 2);executor.setQueueCapacity(CPU_COUNT * 100);executor.setThreadNamePrefix("async-");executor.initialize();return executor;}@Overridepublic Executor getAsyncExecutor() {return asyncExecutor();}@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return new SimpleAsyncUncaughtExceptionHandler();}
}@Service
public class AsyncService {@Autowired@Qualifier("asyncExecutor")private TaskExecutor taskExecutor;public void asyncTask() {taskExecutor.execute(() -> {// 异步任务执行的逻辑});}
}

手动设置线程池,就要合理设置最大线程数和核心线程数,按照网上大多数的说法,都是跟服务器的CPU有关:

一、设置核心线程数corePoolSize

1.先看下机器的CPU核数,然后在设定具体参数:
System.out.println(Runtime.getRuntime().availableProcessors());
即CPU核数 = Runtime.getRuntime().availableProcessors()
2.分析下线程池处理的程序是CPU密集型,还是IO密集型
IO密集型:大量网络,文件操作
IO密集型:核心线程数 = CPU核数 * 2
CPU 密集型:大量计算,cpu 占用越接近 100%, 耗费多个核或多台机器
CPU密集型:核心线程数 = CPU核数 + 1
注:IO密集型(某大厂实践经验)
核心线程数 = CPU核数 / (1-阻塞系数) 例如阻塞系数 0.8,CPU核数为4
则核心线程数为20

 maxPoolSize:
当系统负载达到最大值时,核心线程数已无法按时处理完所有任务,这时就需要增加线程。每秒200个任务需要20个线程,那么当每秒达到1000个任务时,则需要(1000-queueCapacity)*(20/200),即60个线程,可将maxPoolSize设置为60。还有说法就是 cpuNUM*2 或者是cpuNUM*4

keepAliveTime:
线程数量只增加不减少也不行。当负载降低时,可减少线程数量,如果一个线程空闲时间达到keepAliveTiime,该线程就退出。默认情况下线程池最少会保持corePoolSize个线程。

allowCoreThreadTimeout:
默认情况下核心线程不会退出,可通过将该参数设置为true,让核心线程也退出。

queueCapacity:
任务队列的长度要根据核心线程数,以及系统对任务响应时间的要求有关。队列长度可以设置为(corePoolSize/tasktime)*responsetime: (20/0.1)*2=400,即队列长度可设置为400。
队列长度设置过大,会导致任务响应时间过长,切忌以下写法:
LinkedBlockingQueue queue = new LinkedBlockingQueue();
这实际上是将队列长度设置为Integer.MAX_VALUE,将会导致线程数量永远为corePoolSize,再也不会增加,当任务数量陡增时,任务响应时间也将随之陡增。

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

网络爬虫框架Scrapy的入门使用

Scrapy的入门使用 Scrapy概述引擎&#xff08;Engine&#xff09;调度器&#xff08;Scheduler&#xff09;下载器&#xff08;Downloader&#xff09;SpiderItem Pipeline 基本使用安装scrapy创建项目定义Item数据模型对象创建爬虫(Spider)管道pipeline来保存数据启动爬虫 其他…

什么是拧紧扭矩——SunTorque智能扭矩系统

智能扭矩系统-智能拧紧系统-扭矩自动控制系统-SunTorque 拧紧扭矩是紧固件&#xff08;如螺栓、螺母等&#xff09;在拧紧过程中施加在螺纹上的力矩&#xff0c;用于使紧固件产生预紧力&#xff0c;从而保证连接的可靠性和安全性。拧紧扭矩是紧固件连接中非常重要的一个参数&a…

数据更新了,vue的界面没有更新?大写的why到底是为什么!!!

最近工作中&#xff0c;遇见了两个小问题&#xff0c;一时没有反应过来&#xff0c;特此写博客记录一下。希望下次秒反应&#xff01;&#xff01; 有一个界面&#xff0c;打开的时候&#xff0c;由于界面显示内容过多导致而出现了滚动条。第一次浏览窗口时&#xff0c;移动了滚…

qt完成对话框提示

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);//去掉头部this->setWindowFlag(Qt::FramelessWindowHint);//去掉空白this->setAttribute(Qt::WA_Transl…

HTML input 实现回车切换到下一个输入框功能

前言 遇到需求&#xff0c;在客户填写单子时&#xff0c;有多个输入框&#xff0c;为了省事&#xff0c;不需要频繁移动光标填写。 实现效果 实现方式一 HTML <input type"text" name"serialNumber1" onkeydown"cursor(this);"/><in…

是谁?阻止CXL在AI场景大展身手~

CXL虽然被视为业内新宠&#xff0c;但好像在AI场景的应用反而没有得到广泛的响应。 AI场景对内存带宽、容量以及数据一致性有着极高需求&#xff0c;特别是在深度学习训练和推理过程中&#xff0c;大量数据需要在CPU、GPU、加速器以及内存之间快速、高效地流动。CXL作为一种新…

ForkJoinPool、CAS原子操作

ForkJoinPool ForkJoinPool是由JDK1.7后提供多线程并行执行任务的框架。可以理解为一种特殊的线程池。 1.任务分割&#xff1a;Fork&#xff08;分岔&#xff09;&#xff0c;先把大的任务分割成足够小的子任务&#xff0c;如果子任务比较大的话还要对子任务进行继续分割。 …

内网靶机~~dc-2

一、信息收集 1.端口扫描&#xff1a; nmap -sV -p 1-10000 10.1.1.4 2.CMS识别 3.目录扫描&#xff1a; dirsearch http://10.1.1.4/ 4.FLAG1 似乎让我们用cewl生成密码字典&#xff0c;并爆破登录。 cewl -w rewl_passwd.txt http://dc-2/index.php/flag/ 总结&#xff…

离线Linux/openEuler服务器指定本地yum仓库

1、前提准备一个预装坏境比较完整的linux镜像文件&#xff0c;本文服务器使用的是openEuler 官网&#xff1a;openEuler下载 | 欧拉系统ISO镜像 | openEuler社区官网 2、上传镜像文件至服务器 如果是集群服务器&#xff0c;上传其中一台服务器之后&#xff0c;使用scp指令将镜…

数据结构刷题篇 之 【力扣二叉树基础OJ】详细讲解(含每道题链接及递归图解)

有没有一起拼用银行卡的&#xff0c;取钱的时候我用&#xff0c;存钱的时候你用 1、相同的树 难度等级&#xff1a;⭐ 直达链接&#xff1a;相同的树 2、单值二叉树 难度等级&#xff1a;⭐ 直达链接&#xff1a;单值二叉树 3、对称二叉树 难度等级&#xff1a;⭐⭐ 直达…

【滑动窗口】Leetcode 最大连续1的个数 III

题目解析 1004. 最大连续1的个数 III 按照k的数值将0反转成1&#xff0c;记录数组中连续1的最长个数 算法讲解 我们需要一个变量temp记录翻转的次数&#xff0c;每遇到一次0&#xff0c;temp。当temp > k的时候此时说明翻转0已经到达极限&#xff0c;已经不可以在翻转了&…

基于二级片内硬件堆栈的后向CFI 验证方法研究,第三章

随着计算机技术的发展&#xff0c;针对计算机系统的恶意攻击越来越多&#xff0c;造成了巨大的经济损失。面向返回导向编程等恶意攻击方式通过修改堆栈中程序返回地址劫持控制流&#xff0c;达到恶意攻击的目的。后向控制流完整性即返回地址的完整性验证&#xff0c;是一种保护…

Tesla技术方案解析

Tesla技术方案解析 附赠自动驾驶学习资料和量产经验&#xff1a;链接 参考&部分摘选&#xff1a; EatElephant&#xff1a;解读: Tesla Autopilot技术架构 chenq100&#xff1a;TechTips - 031: “Tesla AI Day 2021”学习笔记 All you need to know about Tesla AI Da…

基于单片机的二维码LCD显示控制设计

**单片机设计介绍&#xff0c;基于单片机的二维码LCD显示控制设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的二维码LCD显示控制设计是一个集硬件、软件与通信于一体的综合性项目。此设计的主要目标是实现单片机…

蓝桥备赛——堆队列

AC code import os import sys import heapq a [] b [] n,k map(int,input().split())for _ in range(n):x,y map(int,input().split())a.append(x)b.append(y) q []# 第一种情况&#xff1a;不打第n个怪兽# 将前n-1个第一次所需能量加入堆 for i in range(n-1):heapq.h…

Doris实践——叮咚买菜基于OLAP引擎的应用实践

目录 前言 一、业务需求 二、选型与对比 三、架构体系 四、应用实践 4.1 实时数据分析 4.2 B端业务查询取数 4.3 标签系统 4.4 BI看板 4.5 OLAP多维分析 五、优化经验 六、总结 原文大佬介绍的这篇Doris数仓建设实践有借鉴意义的&#xff0c;这些摘抄下来用作沉淀学…

NFT Insider #125:Astar将与索尼开发的新公链将关注游戏或 NFT 等众多领域

引言&#xff1a;NFT Insider由NFT收藏组织WHALE Members &#xff08;https://twitter.com/WHALEMembers&#xff09;、BeepCrypto &#xff08;https://twitter.com/beep_crypto&#xff09;联合出品&#xff0c;浓缩每周NFT新闻&#xff0c;为大家带来关于NFT最全面、最新鲜…

TR2 - Transformer模型的复现

目录 理论知识模型结构结构分解黑盒两大模块块级结构编码器的组成解码器的组成 模型实现多头自注意力块前馈网络块位置编码编码器解码器组合模型最后附上引用部分 模型效果总结与心得体会 理论知识 Transformer是可以用于Seq2Seq任务的一种模型&#xff0c;和Seq2Seq不冲突。 …

STL —— vector(1)

博主首页&#xff1a; 有趣的中国人 专栏首页&#xff1a; C专栏 本篇文章主要讲解vector使用的相关内容 1. vector简介 vector 是 C 标准库中的一个容器类模板&#xff0c;它提供了动态数组的功能&#xff0c;可以方便地管理和操作元素的集合。下面是关于 vector 的一些基本信…

NRF24L01P和SI24R1的区别

NRF24L01无线模块广泛地运用于&#xff1a;无线门禁、无线数据通讯、安防系统、遥控装置、遥感 勘测、智能运动设备、工业传感器&#xff1b;平常我们用到的无线鼠标基本上采用的都是NORDIC的N RF24L01无线模块方案&#xff0c;而且&#xff0c;只需要一个5号电池即可。 几年前…