业务场景

如果,我先用Key*,去模糊匹配Redis的key,返回出来的key,有3000个,这时候我需要一个for循环读取key的数据,3000条,并且把读取出来的数据,写入同一个List。只用一个for循环同步处理,就会花费很长时间,但是,如果使用多线程异步去读取,那么,时间会大大缩减。

代码实现

SpringBoot应用中需要添加@EnableAsync注解,来开启异步调用,一般还会配置一个线程池,异步的方法交给特定的线程池完成,如下:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;/*** 线程池初始化类** @author huangJunHao* @date 2022/3/18*/
@Configuration
@EnableAsync
public class AsyncConfiguration {@Value(value = "${threadPool.corePoolSize}")private int corePoolSize;@Value(value = "${threadPool.maxPoolSize}")private int maxPoolSize;@Value(value = "${threadPool.queueCapacity}")private int queueCapacity;@Value(value = "${threadPool.keepAliveSeconds}")private int keepAliveSeconds;@Value(value = "${threadPool.threadNamePrefix}")private String threadNamePrefix;@Bean("doSomethingExecutor")public Executor doSomethingExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 核心线程数:线程池创建时候初始化的线程数executor.setCorePoolSize(corePoolSize);// 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程executor.setMaxPoolSize(maxPoolSize);// 缓冲队列:用来缓冲执行任务的队列executor.setQueueCapacity(queueCapacity);// 允许线程的空闲时间60秒:当超过了核心线程之外的线程在空闲时间到达之后会被销毁executor.setKeepAliveSeconds(keepAliveSeconds);// 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池executor.setThreadNamePrefix(threadNamePrefix);// 缓冲队列满了之后的拒绝策略:由调用线程处理(一般是主线程)executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}}

使用的方式非常简单,在需要异步的方法上加@Async注解

import com.alibaba.fastjson.JSONObject;
import com.watsons.onstore.common.dto.Result;
import com.watsons.onstore.common.utils.EmptyUtil;
import com.watsons.onstore.common.utils.ResultUtil;
import com.watsons.onstore.redis.config.RedisKey;
import com.watsons.onstore.redis.pojo.User;
import com.watsons.onstore.redis.service.IRedisService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;/*** 测试类** @author huangJunHao* @date 2022/3/18*/
@RestController
public class TestController {@Autowiredprivate IRedisService redisService;@Autowiredprivate AsyncService asyncService;private final Logger logger = LoggerFactory.getLogger(this.getClass());private List<User> userList;/*** 测试插入redis*/@RequestMapping("/set/redis/list")void setRedisList() {for (int i = 0; i < 1000; i++) {String key = RedisKey.getRedisKey("6666", RedisKey.PICK_ORDER_LIST, i + "", System.currentTimeMillis() + "");User user = new User();user.setEmpNo("EmpNo" + i);user.setEmail("Email" + i);redisService.set(key, JSONObject.toJSONString(user));}}/*** 测试根据模糊key获取redis数据,返回一个List*/@RequestMapping("/get/redis/list/thread")Result getRedisListThread() {//要把list变成线程安全的容器,否则会数据不正确userList = Collections.synchronizedList(new ArrayList<>());String key = RedisKey.getRedisKey("6666", RedisKey.PICK_ORDER_LIST, "*");Set<String> keys = redisService.keys(key);System.out.println("keys:" + keys.size());List<Future> futureList = new ArrayList<>();if (EmptyUtil.notEmpty(keys)) {int j = 1;for (String s : keys) {try {j++;//下面这个方法在方法上使用了@Async注解的Future future = asyncService.doSomething(s, j);futureList.add(future);} catch (Exception e) {e.printStackTrace();}}//判断进程是否全部结束while (true) {if (null != futureList) {boolean isAllDone = true;for (Future future : futureList) {if (null == future || !future.isDone()) {isAllDone = false;}}if (isAllDone) {break;}}}}System.out.println("userList:" + userList.size());return ResultUtil.renderSuccess(userList);}@Servicepublic class AsyncService {@Async("doSomethingExecutor")Future doSomething(String s, int i) {//logger.info("开始获取user,{}", i);try {User entity = redisService.getEntity(s, User.class);userList.add(entity);} catch (Exception e) {e.printStackTrace();}//返回需要用AsyncResult类,我也没试过其他的return new AsyncResult(null);}}/*** 测试根据模糊key获取redis数据,返回一个List*/@RequestMapping("/get/redis/list")Result getRedisList() {String key = RedisKey.getRedisKey("6666", RedisKey.PICK_ORDER_LIST, "*");try {List<User> list = redisService.getFuzzyList(key, User.class);System.out.println("getRedisList:" + list.size());return ResultUtil.renderSuccess(list);} catch (Exception e) {e.printStackTrace();}return ResultUtil.renderSuccess();}
}

结论

getRedisList:这是一个同步方法,从redis获取1000条数据耗时20S
getRedisListThread:这是一个异步方法,从redis获取1000条数据耗时1.5S

注意事项

@Async注解会在以下几个场景失效,也就是说明明使用了@Async注解,但就没有走多线程。

1、异步方法使用static关键词修饰;
2、异步类不是一个Spring容器的bean(一般使用注解@Component和@Service,并且能被Spring扫描到);
3、SpringBoot应用中没有添加@EnableAsync注解;
4、在同一个类中,一个方法调用另外一个有@Async注解的方法,注解不会生效。原因是@Async注解的方法,是在代理类中执行的。

需要注意的是: 异步方法使用注解@Async的返回值只能为void或者Future及其子类,当返回结果为其他类型时,方法还是会异步执行,但是返回值都是null

SpringBoot使用多线程处理任务相关推荐

  1. springboot使用多线程处理高并发接口

    1.同步转异步,单条转批量 2.异步线程获取数据 关于Future同步.异步获取线程执行结果分析_红鲤鱼与彩虹的博客-CSDN博客 多任务并行.利用CompletionService计算任务执行结果的 ...

  2. Springboot定时任务【多线程处理】

    对于服务端同时开启多个定时任务的需求,按照普通的操作方式,springboot单线程的处理方式会造成许多问题,比如两个定时任务时间冲突,只能等一个执行完成在执行另一个:当一个定时任务出现问题,另一个定 ...

  3. 详解 springboot 多线程处理任务无法注入bean

    项目场景: 提示:这里简述项目相关背景: 例如:项目场景:示例:通过蓝牙芯片(HC-05)与手机 APP 通信,每隔 5s 传输一批传感器数据(不是很大) 问题描述:在多线程处理任务时,注入servi ...

  4. springboot2 多线程写入数据_解决SpringBoot项目使用多线程处理任务时无法通过@Autowired注入bean问题...

    {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],&q ...

  5. 零基础学习SpringBoot

    注:本系列教程,又全部进行了更为深入的重写,可关注公众号:java4all,或进入SpringBoot教程,查看最新资料. 本篇是零基础学习SpringBoot专栏的目录,随时更新,可直接点击相应链接 ...

  6. 【二十六】springboot实现多线程事务处理

     springboot篇章整体栏目:  [一]springboot整合swagger(超详细 [二]springboot整合swagger(自定义)(超详细) [三]springboot整合token ...

  7. 【Other】最近在研究的, Java/Springboot/RPC/JPA等

    我的Springboot框架,欢迎关注: https://github.com/junneyang/common-web-starter Dubbo-大波-服务化框架 dubbo_百度搜索Dubbo与 ...

  8. 【二十四】springboot使用EasyExcel和线程池实现多线程导入Excel数据

      springboot篇章整体栏目:  [一]springboot整合swagger(超详细 [二]springboot整合swagger(自定义)(超详细) [三]springboot整合toke ...

  9. 虹软人脸识别-SpringBoot集成

    一.前言 人工智能时代的到来,相信大家已耳濡目染,虹软免费,离线开放的人脸识别 SDK,正推动着全行业进入刷脸时代.为了方便开发者接入,虹软提供了多种语言,多种平台的人脸识别SDK的支持,使用场景广泛 ...

最新文章

  1. 顺F分享,你是在裸奔吗?
  2. Random:产生随机数的类
  3. 联合国发布AI报告:自动化和AI对亚洲有巨大影响【附报告下载】
  4. 在不是Thread类的子类中,如何获取线程对象的名称呢?
  5. 为什么 MySQL的常用引擎都默认使用 B+ 树作为索引?
  6. 2018先知白帽大会 | 议题解读
  7. 2021快手美妆行业数据营销报告
  8. MySQL:数据库导入
  9. jq 展示数据小细节
  10. mysql基础之视图、事务、索引、外键
  11. linux操作实例,linux下的一些文档操作实例 | Soo Smart!
  12. html文字logo
  13. C-COT跟踪算法在OTB数据集上测试的接口函数
  14. ABAP CLEAR REFRESH FREE 说明(刘欣)
  15. Java编程到底是用idea好还是eclipse好?
  16. 1.10 新概念 have a cold/headache
  17. 酷比魔方IWork1X 的做系统问题
  18. clion设置为中文_手把手教你去除CLion的中文字符乱码
  19. 微信小程序制作顶部导航栏
  20. 最全最好用的vim配置(即.vimrc文件的配置),附加vim操作技巧

热门文章

  1. AE模板-LOGO文字动态效果展示
  2. ff14优雷卡补正什么意思_如何评价FF14 禁地优雷卡?
  3. git push报错: nt: Updates were rejected because a pushed branch tip is behind its remote
  4. Java游戏项目之王者荣耀
  5. ios音乐播放器-仿QQ音乐
  6. 浅谈安全管理平台中的事件关联分析
  7. easyui html5主题,详解easyui 切换主题皮肤
  8. 适合中介的二手房房源管理系统
  9. 去丹东绿江村观光几月份合适,油菜花什么时候开,花期多久?
  10. 模拟银行转账取款存款