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://www.hqwc.cn/news/577239.html

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

相关文章

什么是DevOps?如何使用DevOps?

无论您是在维持公司基础设施的正常运行&#xff0c;还是在为客户的IT问题管理提供支持&#xff0c;抑或是在构建、测试或修复软件&#xff0c;还是在保护同事免受安全威胁&#xff0c;您都可能接触过 DevOps。 毕竟&#xff0c;这个术语已经出现了 15 年&#xff0c;其采用率也…

C#_泛型_委托

文章目录 泛型泛型的使用泛型的约束委托委托的实例化多播委托委托的调用内置委托类型委托练习泛型委托Lambda表达式(进阶)上期习题答案本期习题 泛型 泛型&#xff08;Generic&#xff09; 是一种规范&#xff0c;它允许我们使用占位符来定义类和方法&#xff0c;编译器会在编…

基于单片机病房呼叫系统数码管显示房号设计

**单片机设计介绍&#xff0c;基于单片机病房呼叫系统数码管显示房号设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机病房呼叫系统数码管显示房号设计概要主要涵盖了利用单片机技术实现病房呼叫系统&#xff0c;并…

工厂数字化看板是什么?部署工厂数字化看板有什么作用?

随着工业4.0时代的来临&#xff0c;数字化转型已成为制造业发展的必然趋势。在这个背景下&#xff0c;工厂数字化看板作为一种高效的信息展示与管理工具&#xff0c;正逐渐受到越来越多企业的青睐。那么&#xff0c;什么是工厂数字化看板&#xff1f;部署工厂数字化看板又有哪些…

2024软件设计师备考讲义——(3)

程序设计语言 一、程序语言基础 1.基本概念 低级语言&#xff1a;机器语言、汇编语言高级语言&#xff1a; Fortran 科学计算&#xff0c;执行效率高Pascal 为教学开发&#xff0c;表达能力强Prolog 逻辑性程序设计语言C语言 指针操作能力强&#xff0c;可以开发系统级软件&a…

vue实现文字一个字一个字的显示(开箱即用)

图示&#xff1a; 核心代码 Vue.prototype.$showHtml function (str, haveCallback null) {let timeFlag let abcStr for (let i 0; i < str.length; i) {(function (i) {timeFlag setTimeout(function () {abcStr str[i]haveCallback(abcStr)if ((i 1) str.length…

JUC:Monitor 与 Java对象头的内容与锁关系

文章目录 Monitorjava对象头Monitor&#xff08;锁、管程&#xff09; Monitor java对象头 普通对象 Mark Word 主要用来存储对象自身的运行时数据、klass word就是指向该对象的类型。 数组对象 mark word 不同对象状态下结构和含义不同。 Monitor&#xff08;锁、管…

JavaEE 初阶篇-深入了解多线程等待与多线程状态

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 线程等待 1.1 线程等待 - join() 方法 1.1.1 main 线程中等待多个线程 1.1.2 main 线程等待 t2 线程且t2 线程等待 t1 线程 1.1.3 其他线程阻塞等待 main 线程 1.…

期货开户要找到适合自己的系统

物有一个生物圈&#xff0c;大鱼吃小鱼&#xff0c;小鱼吃虾。在期货市场这条生物圈里面&#xff0c;大部分人就是期货市场的虾子&#xff0c;是被吃的&#xff0c;所以必须成长起来&#xff0c;往更高一层走&#xff0c;到可以吃虾子的时候&#xff0c;就是挣钱的时候。学习不…

物流监控升级,百递云·API开放平台助力某电商平台实现高效物流管理

不论是电商平台自身还是消费者&#xff0c;都有着对物流监控的强烈需求。 消费者下单后be like: 每十分钟看一次快递轨迹 放心&#xff0c;电商平台也一样关心物流状态&#xff01;怎样第一时间向用户传递物流状态&#xff1f; 怎么减少重复的提问和投诉&#xff1f;怎样监管…

2023年EI会议论文已见刊/检索进展汇总

2023年录用的会议论文已在SPIE、ACM、IEEE等出版社正式上线见刊&#xff0c;并已陆续完成EI Compendex数据库收录&#xff0c;详情如下&#xff1a; EIECT 2023——IEEE出版&#xff0c;并完成EI收录 会议信息&#xff1a; 第三届电子信息工程与计算机技术国际学术会议&…

cesium vue 绘制标记实体(撒点),监听鼠标左击事件

添加实体 const viewer new Cesium.Viewer(cesiumContainer, {})viewer.entities.add()查看实体 const viewer new Cesium.Viewer(cesiumContainer, {}) const billboard viewer.entities.add({...})viewer.zoomTo(billboard)删除实体 根据实体删除 if (billboard.value…