范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

Springboot异步多线程编程

  一、基础知识
  同步 :同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;
  异步 :异步是指进程不需要一直等下去,而是继续执行下面的操作。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。
  进程 :进程是独立的应用程序,占用cpu资源和物理内存。一个进程包括由操作系统分配的内存空间,包含一个或多个线程;
  线程 :线程是进程中虚拟的时间片,一个线程不能独立的存在,它必须是进程的一部分。
  多线程 :实际上就是时间片的轮转或者抢占。多线程能满足编写高效率的程序来达到充分利用CPU的目的; 二、什么时候用同步&异步
  什么时候用同步 :如果数据在线程间共享,例如正在写的数据可能被另外一个线程读到,而正在读的数据可能被另外一个线程写到,这些数据是共享的数据。这时就必须进行同步存取操作,否者前后读取的数据就有可能不一致;
  什么时候用异步 :调用一个需要花费很长时间来执行的方法的时候,并且不需要让程序等待对方返回,这时就应该使用异步编程;
  必须使用同步的场景举例 :
  有一个共享的银行账号,原来里面有余额1000元,现在有两个用户A,B都要进行取钱;
  首先A查询账号剩余1000元,A想要取出200元,A点击取款,系统正在处理取款事项…
  紧接着在A取款的过程中B查询同一个账号还有1000元,B也想要取走200元;
  A取完款后剩余800元,正常。而B取完款后理论上应该剩余600元,但是实际上还是剩余800元。这种场景就必须使用同步,而不能使用异步; 三、什么时候需要使用多线程
  举个例子 :
  假设有个请求,服务端的处理需要执行3个比较耗时的操作:
  1、操作1(200ms)
  2、操作2(200ms)
  3、操作3(200ms)
  单线程总共就需要600ms,但如果把操作1、操作2、操作3分别分给3个线程去做,就只需要200ms了。
  但是假设另外一个请求,服务端的处理也需要执行3个操作:
  1、操作1(10ms)
  2、操作2(10ms)
  3、操作3(400ms)
  单线程总共就需要420ms,这种情况下,即使把操作1、操作2、操作3分别分给3个线程去做,也需要400ms(耗时取决于最慢的那个线程的执行速度)。比起不用单线程,只节省了20ms。但是有可能线程调度切换也要花费个1、2ms。因此,这个方案显得优势就不明显了,还带来程序复杂度提升,不太值得,此时更好的方案是去优化降低操作3的耗时。 四、Springboot异步多线程编程实现
  4.1 使用idea创建springboot web项目,工程最终目录结构如下 :
  4.2 首先创建springboot的线程池配置 :
  common包下面创建ExecutorConfig类,用于自定义线程池的相关配置。使用@Configuration和@EnableAsync这两个注解,表示这是线程池的配置类。 @Configuration @EnableAsync @Slf4j public class ExecutorConfig {      /** 核心线程数(默认线程数) */     private int corePoolSize = 10;     /** 最大线程数 */     private int maxPoolSize = 20;     /** 允许线程空闲时间(单位:默认为秒) */     private static final int keepAliveTime = 60;     /** 缓冲队列大小 */     private int queueCapacity = 10;      @Bean     public Executor asyncServiceExecutor(){         log.info("start asyncServiceExecutor");         ThreadPoolTaskExecutor executor=new ThreadPoolTaskExecutor();         //配置核心线程数         executor.setCorePoolSize(corePoolSize);         //配置最大线程数         executor.setMaxPoolSize(maxPoolSize);         //配置空闲时间         executor.setKeepAliveSeconds(keepAliveTime);         //配置队列大小         executor.setQueueCapacity(queueCapacity);         //配置线程前缀名         executor.setThreadNamePrefix("async-service-");          // rejection-policy:当pool已经达到max size的时候,如何处理新任务         // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());          //执行初始化         executor.initialize();         return executor;     } }
  4.3 service层接口和实现 :
  service包下新增server层的接口AsyncService类和对应的实现类AsyncServiceImpl。AsyncService内容如下: public interface AsyncService {     /**      * 执行异步任务      **/     void executeAsync(); }
  AsyncServiceImpl类内容如下,注意:
  1.在executeAsync方法上增加注解@Async("asyncServiceExecutor") ;
  2.@Async表示使用异步实现方式
  3.括号里的asyncServiceExecutor是前面ExecutorConfig.java中的方法名,表明executeAsync方法使用asyncServiceExecutor方法创建的线程池多线程执行: @Slf4j @Service public class AsyncServiceImpl implements AsyncService {      //异步多线程调用     @Async("asyncServiceExecutor")     public void executeAsync() {         log.info("start executeAsync");         try{             Thread.sleep(2000);         }catch (Exception e){             e.printStackTrace();         }         log.info("end executeAsync");     } }
  4.4 controller层实现 :
  创建HelloController类,新增/test http接口,调用server层的executeAsync服务。 @Slf4j @RestController public class HelloController {     @Autowired     private AsyncService asyncService;      //异步多线程调用方法,不用等方法返回结果     @RequestMapping("/test")     public String test(){         log.info("start submit");          //调用service层的任务         asyncService.executeAsync();          log.info("end submit");         return "success";     } }
  4.5 验证效果 :
  验证异步效果 :
  1.先将ExecutorConfig类下corePoolSize设置为1,表示只用1个线程。然后运行springboot;
  2.springboot启动成功后,在浏览器输入:http://localhost:8080/test 。 可以看到虽然我们前面AsyncServiceImpl代码中sleep了2秒,但由于使用的是异步实现,所以接口马上直接先返回了success,而不需要等待2秒后再返回。
  后台日志也能看到,异步接口controller层很快就执行结束,然后service方法继续按代码执行了2秒:
  验证多线程效果 :
  1.corePoolSize设置为1时,使用Jmeter同时调用接口:http://localhost:8080/test 4次;
  2.在springboot的控制台看见日志如下:
  可以看出是1个线程每隔2秒执行完一次start&end executeAsync, 执行4次总共花费了8秒时间;
  3.将corePoolSize设置为10,重启sprintboot;
  4.再次使用Jmeter同时调用接口:http://localhost:8080/test 4次;
  5.在springboot的控制台看见日志如下:
  可以看出是4个线程同时在执行,执行完4次start&end executeAsync, 总共花费了2秒时间,这就是多线程可以提高程序运行效率的体现。
  4.6 获取多线程的返回值 :
  Java自jdk1.5以后,提供了java.util.concurrent.Future来获取异步线程返回的结果。 主线程会创建一个 Future 接口的对象,然后启动并发线程,并告诉并发线程,一旦你执行完毕,就把结果存储在这个 Future 对象里。
  一般情况下,我们会把长时间运行的逻辑放在异步线程中进行处理,这是使用 Future 接口最理想的场景。主线程只要简单的将异步任务封装在 Future 里,然后开始等待 Future 的完成,在这段等待的时间内,可以处理一些其它逻辑,一旦 Future 执行完毕,就可以从中获取执行的结果并进一步处理。
  AsyncServiceImpl类中增加两个方法: //多线程调用并获取回调结果     @Async("asyncServiceExecutor")     public Future sendMessageAsync1(){         log.info("异步发送消息1---执行开始");         try {             Thread.sleep(2000);         } catch (InterruptedException e) {             e.printStackTrace();         }         log.info("异步发送消息1---执行结束");         return new AsyncResult<>("异步发送消息1");     }          @Async("asyncServiceExecutor")     public Future sendMessageAsync2(){         log.info("异步发送消息2---执行开始");         try {             Thread.sleep(2000);         } catch (InterruptedException e) {             e.printStackTrace();         }         log.info("异步发送消息2---执行结束");         return new AsyncResult<>("异步发送消息2");     }
  AsyncService类中增加接口: Future sendMessageAsync1();     Future sendMessageAsync2();
  Controller中增加http接口调用: //异步多线程调用,但是要等方法回调结果。用多线程,所以只需要2秒     @RequestMapping("/sendMessageAsync")     public String sendMessageAsync() throws ExecutionException, InterruptedException {         System.out.println("开始时间:"+new Date());         Future sendMessageAsync1 = asyncService.sendMessageAsync1();         Future sendMessageAsync2 = asyncService.sendMessageAsync2();          String result="";         String result1="";         String result2="";          while(!(sendMessageAsync1.isDone() && sendMessageAsync2.isDone())){ //            System.out.println( //                    String.format( //                            "future1 is %s and future2 is %s", //                            sendMessageAsync1.isDone() ? "done" : "not done", //                            sendMessageAsync2.isDone() ? "done" : "not done" //                    ) //            ); //            Thread.sleep(300);         }          result +=sendMessageAsync1.get();         result +=sendMessageAsync2.get();          System.out.println("结束时间:"+new Date());         return result;
  上面使用的是先调用 Future.isDone() 判断任务是否完成,再调用 Future.get() 从完成的任务中获取任务执行的结果。
  也可以直接用Future.get()并设置一个超时时间: @RequestMapping("/sendMessageAsync")     public String sendMessageAsync() throws ExecutionException, InterruptedException {         System.out.println("开始时间:"+new Date());         Future sendMessageAsync1 = asyncService.sendMessageAsync1();         Future sendMessageAsync2 = asyncService.sendMessageAsync2();          String result="";         String result1="";         String result2="";          //通过future.get()方法阻塞性获取执行结果,设置超时时间为3秒,3秒还没获取到值,就超时报错         try {             result1=sendMessageAsync1.get(3000, TimeUnit.MILLISECONDS);         } catch (TimeoutException e) {             sendMessageAsync1.cancel(true);             log.error("sendMessageAsync1方法超时未返回结果");             e.printStackTrace();         }          try {             result2=sendMessageAsync2.get(3000, TimeUnit.MILLISECONDS);         } catch (TimeoutException e) {             sendMessageAsync2.cancel(true);             log.error("sendMessageAsync2方法超时未返回结果");             e.printStackTrace();         }         result=result1+result2;                  System.out.println("结束时间:"+new Date());         return result;     }
  Future.get() 方法是一个阻塞方法。如果任务还没执行完毕,那么会一直阻塞直到直到任务完成
  为了防止调用 Future.get() 方法阻塞当前线程,推荐的做法是先调用 Future.isDone() 判断任务是否完成,然后再调用 Future.get() 从完成的任务中获取任务执行的结果。
  因为 Future.isDone() 和 Future.get() 的存在,我们就可以在等待任务完成时运行其它一些代码。使用 isDone() 和 get() 方法来获取结果,这应该是消费 Future 最常见的方式。
  针对上面的代码, 如果不用isDone(),直接用get(), 那么get()阻塞的这2秒内就不能做任何其他事情。而用了while isDone(), 这2秒内则可以做一些其他的事情,比如上面代码中的输出打印一段话。
  运行结果如下:
  虽然sendMessageAsync1和sendMessageAsync2都要2秒时间,由于是多线程并行处理,所以总共只花费了2秒。
  正常的单线程处理: //正常的单线程处理,要花4秒     @RequestMapping("/sendmessage")     public String sendMessage() throws ExecutionException, InterruptedException {         System.out.println(new Date());          //调用service层的任务         String sendMessage1=asyncService.sendMessage1();         String sendMessage2=asyncService.sendMessage2();          String result="";         result=sendMessage1+sendMessage2;          System.out.println(new Date());         return result;     }
  单线程顺序执行sendMessage1和sendMessage2,每一个方法执行需要2秒,总共就需要4秒才能执行完。
  =================================
  以上就是本篇文章的全部内容,如果对你有帮助,
  欢迎搜索关注我的微信公众号【程序员杨叔】:测开一枚,持续分享全栈测试知识干货。标签:自动化测试、性能测试、Java、Python、DevOps、CI/CD、小程序测试、测试工具、测试开发、测试框架/平台、测试管理…

加劲科技品牌数字化营销企业可以通过什么手段进行优化?现在进入互联网时代,企业如果想迅速吸引顾客的目光达到自己的成交目的,必然需要进行推广。通过推广吸引客人引流引起了更多的关注。在将企业提示给大众的视野后,客户可以理解企业,关注企业,加劲科技品牌营销推广,互联网思维5个主要误解互联网思维应该是当今最流行的思维方式。毕竟,在互联网思维的指导下,国内外有许多成功的公司,而好的公司多数是互联网公司。互联网思维是个好东西。互联网的发展如此之猛,以至于它将完全改变加劲科技品牌互联网营销,顺应互联网发展变化变化营销策略随着互联网和新媒体的发展,网络营销逐渐取代传统营销。如何持续捕捉用户注意力,成为品牌思考的问题。改变传播方式和内容,吸引用户注意力,激发情感互动,成为互联网品牌营销的重要课题。互联加劲科技品牌营销推广,网站推广的意义网站推广是网络营销的重要组成部分,对电子商务起着至关重要的作用,是企业实现互联网产业转型的重要手段。网站推广方法的方法很多,归根结底离不开互动这个核心,只不过使用的载体和操作方式有加劲科技企业营销推广,传统企业转型如何借力打力做好营销?在当今时代,新技术革命和产业转型即将到来。以互联网,大数据,云计算,人工智能等为代表的下一代信息技术正在迅速发展,在各个领域广泛传播,并不断产生新的产业,新的模式和新的产品。业务形链游真的有未来吗?OEC公链上的GameFi项目不断火热今年AxieInfinity带来的Playtoearn成功引爆GameFi热潮,成为圈内各种场合下的必备谈资,不得不说,承袭去年DeFi热潮中的流动即挖矿精髓是它走向成功的关键要素GameFi链游是传统游戏的新升级电子游戏从诞生起,直到今天依旧是全球最受欢迎的娱乐方式。根据数据显示,在2021年,全球每年传统游戏市场直接收入在1750亿至2200亿美元之间,比全球疫情之前的2019年多出21山东诸城大源建设集团紫檀文苑获中国安装之星众所周知,山东大源建设集团成立于1993年,是一家集房地产开发建筑施工装饰装修钢结构工程物业管理园林绿化生态农业等业态于一体的大型综合性企业集团。2021年以来,山东诸城大源建设集明马克数字资产交易平台了解地方需求,提供协助共渡难关9月,疫情无情地席卷了东南亚,整个东南亚处在水深火热之中,越南受到Delta变异病毒肆虐,单日确诊人数屡破万人,当局下令包括胡志明市河內市在内的多处城市采取封城措施,盼能尽快遏止疫欧易NFT市场让NFT不再是少数人的游戏一件件天价作品,让NFT市场充斥着暴富的想象。伴随着NFT的爆火,普通投资者跃跃欲试。但现在的情况,NFT好像只是少数人的财富游戏。一方面,从目前动辄万美元计价的单位来看,要进行相NFT营销的新发展,选欧易NFT市场关心艺术界的人们,恐怕都会注意到最近在海外掀起的NFT热。由拍卖行盲盒到球鞋,NFT迅速出现在各个领域,并吸引了腾讯阿里等巨头的下场,数字视觉艺术家BeepleNFT艺术作品展将于
华为苹果小米这也许是王家卫眼中的手机森林不知道从什么时候开始,在什么东西上面都有个日期,秋刀鱼会过期,肉罐头会过期,连保鲜纸都会过期,我开始怀疑,在这个世界上,还有什么东西是不会过期的?重庆森林有一个问题不知道你有没有想诺基亚概念新机曝光波浪屏屏下前摄7英寸,还有140W快充近几年,诺基亚手机逐渐边缘化,销量与巅峰时期早已相差甚远,但是论概念机设计诺基亚从来都没输过。近日,有消息人士曝光了一组诺基亚概念新机的渲染图,从图片内容来看,该机正main为一块电商实体店,强强联手,共赢才能共生任何红利都有消失的一天,电商也不例外。在同质化加剧,拼价格的电商平台,由于门槛低,鱼龙混杂,产品质量售后服务等参差不齐,严重影响消费者的购买体验,导致其逐渐对电商平台失去信心,从而红米官宣RedmiBuds3耳机,9月6日发布9月3日消息,在今日Redmi官方在社交平台发布消息称,即将推出Redmi首款半入耳式耳机RedmiBuds3,除了半入耳设计外,还采用了小方盒外观。而在放出的海报中我们可以看到,像素高达2亿,尺寸巨大,三星HP1芯片谁会首发?手机镜头的像素可谓从一诞生就进入了快速增长,从早年的30万100万像素,到今天的动辄6400万1亿像素,前前后后不过20年,也就是一代人时间。而刚刚业内又诞生了一款超新星级别的感光小米12信息揭秘,三主摄首发骁龙898小米MIX4才刚发布没多久,就有小米12的信息疑似曝光了也可以看出米粉的对小米的关注有多高了。想换新手机的,也可以把旧手机放Z转上回收了,价格高不出所料,小米12将搭载基于5纳米制一分钟资讯传音VIVO红魔谷歌三星OPPO,最新消息哈喽,我是毛小毛。关注我,每日带你了解最新数码资讯!一分钟资讯第125期,感谢你的阅读。最新中高端手机性能TOP10近日,安兔兔更新了2021年8月份Android中高端手机性能榜区块链入门108个知识点区块链入门必备108知识点(欢迎同频者交流)1什么是区块链把多笔交易的信息以及表明该区块的信息打包放在一起,经验证后的这个包就是区块。每个区块里保存了上一个区块的hash值,使区块微信iOS8。0正式版更新余额宝第一名存了2。2亿网易云去除独家微信iOS8。0。13正式版更新iOS平台的微信现已推出新版本。本次更新是正式版更新,版本号从8。0。12更新至8。0。13。此次版本更新似乎依然侧重于视频号和直播功能的调整,例如8月旗舰手机性能排行,你的手机上榜了吗?近日安兔兔发布了8月Android旗舰手机性能排行榜,来看看你的手机上榜了吗?黑鲨4Pro,跑分86。4万分iQOO8Pro,跑分84。6万分红魔6Pro,跑分82。9万分iQOO高通骁龙870手机的选择今年的手机市场可谓是包罗万象,唯一不变的是骁龙888手机的发热问题却成为了一条定律,各家厂商为了这烫手的温控也是绞尽脑汁,而对于消费者来说,这几年手机芯片市场格局发生着巨大的变化,