面试官问我Java异步编程用过吗?我直接说了6种方式!
文章目录
在实际开发中有些耗时操作,或者对主流程不是那么重要的逻辑,可以通过异步的方式去执行,从而提高主逻辑的效率。常见的场景比如下单成功后短信或者小程序内通知用户,这个过程其实可以走异步,最坏的情况是没通知到用户,这个情况是可以接受的,只要下单成功了就行。下面介绍几种常见的异步编程的方式:
PS:忽略下方创建线程池的方式,主要看如何实现异步编程
线程池 + Runnable/Callable
这种方式在主线程中引入线程池,通过线程池进行异步操作。
public class AsyncThread{private static ExecutorService executorService = Executors.newSingleThreadExecutor();public static void main(String[] args) {System.out.println("主线程开始");executorService.submit(() -> {System.out.println("这是一个异步线程");});System.out.println("主线程结束");}
}-- 控制台打印结果
主线程开始
主线程结束
这是一个异步线程
可能有人有疑问为什么不直接new Thread(),主要原因是频繁创建线程,销毁非常耗费系统资源。线程池是池化技术,可以更好的管理池内线程的生命周期。但是这种实现方式不能满足一些特殊场景,比如需要异步任务的返回值。
线程池 + Future
Future是JUC并发包提供的,它的出现解决了异步任务需要返回值的问题。
public class FutureTest {ExecutorService executorService = Executors.newFixedThreadPool(1);public static void main(String[] args) {System.out.println("主线程开始");new FutureManager().execute();System.out.println("主线程结束");}@SneakyThrowspublic String execute() {Future<String> future = executorService.submit(new Callable<String>() {@Overridepublic String call() throws Exception {System.out.println("这是一个异步线程开始");Thread.sleep(2000);System.out.println("这是一个异步线程结束");return "这是一个异步线程返回的结果";}});String result = "默认返回值";// 放开注释,会阻塞主线程
// result = future.get();return result;}
}-- 不获取结果 控制台打印结果
主线程开始
主线程结束
这是一个异步线程开始
这是一个异步线程结束-- 阻塞获取结果 控制台打印结果
主线程开始
这是一个异步线程开始
这是一个异步线程结束
主线程结束
Future虽然可以获得异步任务的结果,但是缺点也很明显,主要缺点如下:
- 获取结果需要阻塞主线程
- 异步任务出现异常,主线程无法感知
- 多个Future之间相互独立,如果多个异步任务的返回值有依赖关系,就不能满足需求
CompletableFuture
CompletableFuture也是JUC并发包中的类,它可以让多个Future进行编排。
public class CompletableFutureTest {/*** thenAccept子任务和父任务公用同一个线程*/@SneakyThrowspublic static void thenRunAsync() {CompletableFuture<Integer> fristFuture = CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread() + " fristFuture ....");try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}return 1;});CompletableFuture<Void> secondFuture = fristFuture.thenRunAsync(() -> {System.out.println(Thread.currentThread() + " secondFuture ...");});//等待任务1执行完成System.out.println("fristFuture结果->" + fristFuture.get());//等待任务2执行完成System.out.println("secondFuture结果->" + secondFuture.get());}public static void main(String[] args) {System.out.println("主线程开始");thenRunAsync();System.out.println("主线程结束");}}-- 控制台打印结果
主线程开始
Thread[ForkJoinPool.commonPool-worker-3,5,main] fristFuture ....
Thread[ForkJoinPool.commonPool-worker-5,5,main] secondFuture ...
fristFuture结果->1
secondFuture结果->null
主线程结束
以上示例fristFuture与secondFuture两个任务简历联系,后者需要前者执行完在执行。可以实际运行一下看下效果。大概流程是示例代码的11行会阻塞在那里,而不会先打印Thread[ForkJoinPool.commonPool-worker-5,5,main] secondFuture … 原因是secondFuture依赖于fristFuture,fristFuture执行结束后才会往下执行。
CompletableFuture没有配合线程池使用的原因是,CompletableFuture默认使用的是ForkJoinPool.commonPool,从打印的结果可以清楚的看出来。ForkJoinPool的好处是可以自己管理线程池,当没有太多任务需要执行时,它会自己关闭一些线程,释放资源。
线程池 + @Async注解
**@Async注解建议配合线程池使用,使用时没有指定线程池,会使用默认的SimpleAsyncTaskExecutor,它并不是真正的线程池,每次都是创建新的线程执行任务,不会复用线程。最主要的是它没最大线程数的限制,并发大的时候容易产生性能问题。**下面是一个示例:
首先需要自定义一个线程池,加上@EnableAsync和@Configuration,这样可以不用在启动类上加@EnableAsync。
@EnableAsync
@Configuration
public class TaskPoolConfig {@Bean("taskExecutor")public Executor taskExecutor() {ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();threadPoolTaskExecutor.setCorePoolSize(Runtime.getRuntime().availableProcessors());threadPoolTaskExecutor.setMaxPoolSize(Runtime.getRuntime().availableProcessors() * 2);threadPoolTaskExecutor.setQueueCapacity(100);threadPoolTaskExecutor.setKeepAliveSeconds(60);threadPoolTaskExecutor.setThreadNamePrefix("taskExecutor-");threadPoolTaskExecutor.setAwaitTerminationSeconds(60);threadPoolTaskExecutor.setRejectedExecutionHandler(new java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy());return threadPoolTaskExecutor;}
}
在需要异步的方法上加上@Async注解,并指定线程池即可。
@Service
public class AsyncServiceImpl implements AsyncService {@Override@Async("taskExecutor")public MessageResult sendSms(String callPrefix, String mobile, String actionType, String content) {// 业务逻辑}}
Spring 事件
Spring的事件原理是在某个地方抛出一个事件,通过Spring的监听机制监听到该事件,进而做出业务逻辑的处理。这个过程需要有3个步骤:创建事件,发布事件,监听事件。
创建事件
首先,我们创建一个自定义的事件,继承自ApplicationEvent:
import org.springframework.context.ApplicationEvent;public class MyEvent extends ApplicationEvent {public MyEvent(Object source) {super(source);}
}
事件发布者
然后,我们创建一个事件发布者,它会发布我们刚刚创建的MyEvent事件:
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;@Component
public class MyEventPublisher {private final ApplicationEventPublisher applicationEventPublisher;public MyEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}public void publishEvent() {MyEvent myEvent = new MyEvent(this);applicationEventPublisher.publishEvent(myEvent);}
}
事件监听器
接下来,我们创建一个事件监听器,它会监听并处理我们的MyEvent事件:
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;@Component
public class MyEventListener {@EventListenerpublic void handleMyEvent(MyEvent event) {System.out.println("MyEvent received");}
}
调用事件
在主程序中调用事件发布者的publishEvent方法来发布事件:
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Main {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.register(MyEventPublisher.class);context.register(MyEventListener.class);context.refresh();MyEventPublisher publisher = context.getBean(MyEventPublisher.class);publisher.publishEvent();}
}
消息队列
消息队列在异步的场景下使用非常的广泛。以下以RabbitMQ为例。
生产者
@Component
public class Producer {@AutowiredAmqpTemplate amqpTemplate;public void sendCallbackMessage(MessageRequest message) {amqpTemplate.convertAndSend(QueueEnum.QUEUE_NAME.getExchange(), QueueEnum.QUEUE_NAME.getRoutingKey(), JSONObject.toJsonString(message), new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws Exception {// xxxxxreturn message;}});}
}
消费者
@Component
@RabbitListener(queues = "message.order", containerFactory = "listenerContainerFactory")
public class Consumer {@RabbitHandlerpublic void handle(String json, Channel channel, @Headers Map<String, Object> map) throws Exception {// 校验逻辑,比如业务校验,请求头检验try { //执行业务逻辑//消息消息成功手动确认,对应消息确认模式acknowledge-mode: manualchannel.basicAck((Long) map.get(Headers.SUCCESS), false);} catch (Exception e) {log.error("消费失败 -> {}", e);}}
}
相关文章:
面试官问我Java异步编程用过吗?我直接说了6种方式!
文章目录 线程池 Runnable/Callable线程池 FutureCompletableFuture线程池 Async注解Spring 事件创建事件事件发布者事件监听器调用事件 消息队列生产者消费者 在实际开发中有些耗时操作,或者对主流程不是那么重要的逻辑,可以通过异步的方式去执行&am…...
一维坐标的移动(bfs)
在一个长度为n的坐标轴上,小S想从A点移动B点。 他的移动规则如下: 向前一步,坐标增加1。 向后一步,坐标减少1。 跳跃一步,使得坐标乘2。 小S不能移动到坐标小于0或大于n的位置。 小S想知道从A点移动到B点的最少步数是多…...
面试题 整理
第1题:常见数据类型大小 这边以64位计算机系统,环境而言。 类型 存储大小 值范围 char 1 字节 -128 到 127 或 0 到 255 unsigned char 1 字节 0 到 255 signed char 1 字节 -128 到 127 int 4 字节 -32,768 到 32,767 或 -2,147,483,648…...
苍穹外卖-day08:导入地址簿功能代码(单表crud)、用户下单(业务逻辑)、订单支付(业务逻辑,cpolar软件)
苍穹外卖-day08 课程内容 导入地址簿功能代码用户下单订单支付 功能实现:用户下单、订单支付 用户下单效果图: 订单支付效果图: 1. 导入地址簿功能代码(单表crud) 1.1 需求分析和设计 1.1.1 产品原型(…...
Java面试相关问题
一.MySql篇 1优化相关问题 1.1.MySql中如何定位慢查询? 慢查询的概念:在MySQL中,慢查询是指执行时间超过一定阈值的SQL语句。这个阈值是由long_query_time参数设定的,它的默认值是10秒1。也就是说,如果一条SQL语句的执…...
Linux Shell中的循环控制语句
Linux Shell中的循环控制语句 在编写Shell脚本时,循环是一种常用的控制结构,用于重复执行一系列命令。在Shell中,主要有三种循环控制语句:for循环,while循环,和until循环。 1. For循环 for循环是最常见的…...
proto3语言指南
Language Guide (proto3) 本指南介绍了如何使用 protocol buffer 语言来构建protocol buffer数据,包括.proto文件语法以及如何从.proto 文件生成数据访问类。它涵盖了proto3 版本的协议缓冲语言:有关proto2语法的信息,请参阅proto2语言指南。 文章目录 Language Guide (pro…...
解决后端传给前端的日期问题
解决方式: 1). 方式一 在属性上加上注解,对日期进行格式化 但这种方式,需要在每个时间属性上都要加上该注解,使用较麻烦,不能全局处理。 2). 方式二(推荐 ) 在WebMvcConfiguration中扩展SpringMVC的消息转…...
MySQL中的索引失效情况介绍
MySQL中的索引是提高查询性能的重要工具。然而,在某些情况下,索引可能无法发挥作用,甚至导致查询性能下降。在本教程中,我们将探讨MySQL中常见的索引失效情况,以及它们的特点和简单的例子。 1. **索引失效的情况** …...
SpringBoot异常:类文件具有错误的版本 61.0, 应为 52.0的解决办法
问题: java: 无法访问org.mybatis.spring.annotation.MapperScan 错误的类文件: /D:/Program Files/apache-maven-3.6.0/repository/org/mybatis/mybatis-spring/3.0.3/mybatis-spring-3.0.3.jar!/org/mybatis/spring/annotation/MapperScan.class 类文件具有错误的…...
Cloudways搭建WordPress外贸独立站完整教程
现在做个网站不比从前了,搭建网站非常的简单,主要是由于开源的CMS建站系统的崛起,就算不懂编程写代码的人也能搭建一个自己的网站,这些CMS系统提供了丰富的主题模板和插件,使用户可以通过简单的拖放和配置操作来建立自…...
关于 闰年 的小知识,为什么这样判断闰年
闰年的规定: 知道了由来,我们就可以写程序来判断: #include <stdio.h> int main() {int year, leap;scanf("%d",&year);if((year%4 0 && year%100 ! 0) || year%400 0)leap 1;else leap 0;if(leap) printf(…...
Elasticsearch:调整近似 kNN 搜索
在我之前的文章 “Elasticsearch:调整搜索速度”,我详细地描述了如何调整正常的 BM25 的搜索速度。在今天的文章里,我们来进一步探讨如何提高近似 kNN 的搜索速度。希望对广大的向量搜索开发者有一些启示。 Elasticsearch 支持近似 k 最近邻…...
UE5数字孪生系列笔记(二)
智慧城市数字孪生系统 制作流云动画效果 首先添加一个图像在需要添加流云效果的位置 添加动画效果让其旋转 这个动画效果是程序开始就要进行的,所以要在EventConstruct中就可以启动这个动画效果 添加一个一样的图像在这里,效果是从此处进行放大消散 添…...
基于vue实现bilibili网页
学校要求的实验设计,基于vue实现bilibili网页版,可实现以下功能 (1)基本的悬浮动画和页面渲染 (2)可实现登录和未登录的页面变化 (3)在登录页面的,实现密码判断,或者短信验证方式的倒数功能 (4)实现轮播图 (5)实现预览视频(GIF) (6)页面下拉到一定高度出现top栏以及右下角的返回…...
计算机二级(Python)真题讲解每日一题:《十字叉》
描述 …...
基于正点原子潘多拉STM32L496开发板的简易示波器
一、前言 由于需要对ADC采样性能的评估,重点在于对原波形的拟合性能。 考虑到数据的直观性,本来计划采集后使用串口导出,并用图形做数据拟合,但是这样做的效率低下,不符合实时观察的需要,于是将开发板的屏幕…...
【Docker】apisix 容器化部署
APISIX环境标准软件基于Bitnami apisix 构建。当前版本为3.8.0 你可以通过轻云UC部署工具直接安装部署,也可以手动按如下文档操作,该项目已经全面开源,可以从如下环境获取 配置文件地址: https://gitee.com/qingplus/qingcloud-platform qi…...
基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的障碍物检测系统(深度学习代码+UI界面+训练数据集)
摘要:开发障碍物检测系统对于道路安全性具有关键作用。本篇博客详细介绍了如何运用深度学习构建一个障碍物检测系统,并提供了完整的实现代码。该系统基于强大的YOLOv8算法,并对比了YOLOv7、YOLOv6、YOLOv5,展示了不同模型间的性能…...
从零开始学HCIA之SDN04
1、VXLAN数据封装 (1)Original L2 Frame,原始以太网报文,业务应用的以太网帧。 (2)VXLAN Header,VXLAN协议新定义的VXLAN头,长度为8字节。VXLAN ID(VNI)为2…...
GET 和 POST 有什么区别?
1.从缓存的角度,GET 请求会被浏览器主动缓存下来,留下历史记录,而 POST 默认不会。 2.从编码的角度,GET 只能进行 URL 编码,只能接收 ASCII 字符,而 POST 没有限制。 3.从参数的角度,GET 一般放…...
Qt学习--继承(并以分文件实现)
基类 & 派生类 一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。定义一个派生类,我们使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名。 总结:简单来说,父类有的,子…...
软考75-上午题-【面向对象技术3-设计模式】-设计模式的要素
一、题型概括 上午、下午题(试题五、试题六,二选一) 每一个设计模式都有一个对应的类图。 二、23种设计模式 创建型设计模式:5 结构型设计模式:7 行为设计模式:11 考试考1-2种。 三、设计模式的要素 3…...
Matlab|面向低碳经济运行目标的多微网能量互联优化调度
目录 主要内容 优化流程 部分程序 结果一览 下载链接 主要内容 该程序为多微网协同优化调度模型,系统在保障综合效益的基础上,调度时优先协调微网与微网之间的能量流动,将与大电网的互联交互作为备用,降低微网与大电…...
3.Gen<I>Cam文件配置
Gen<I>Cam踩坑指南 我使用的是大恒usb相机,第一步到其官网下载大恒软件安装包,安装完成后图标如图所示,之后连接相机,打开软件,相机显示一切正常。之后查看软件的安装目录如图,发现有GenICam和GenTL两个文件&am…...
【兆易创新GD32H759I-EVAL开发板】 TLI(TFT LCD Interface)用法详细介绍
大纲 1. 引言 2. TLI外设特点 3. TLI硬件架构 4. TLI寄存器功能 5. TLI的配置和使用步骤 6. TLI图层概念 7. 图像处理和显示优化 8. 基于GD32H759I-EVAL开发板的TLI应用示例 1. 引言 在当今的嵌入式系统设计中,图形用户界面(GUI)的应…...
恒创科技:什么是BGP线路服务器?BGP机房的优点是什么?
在当今的互联网架构中,BGP(边界网关协议)线路服务器和BGP机房扮演着至关重要的角色。BGP作为一种用于在自治系统(AS)之间交换路由信息的路径向量协议,它确保了互联网上的数据能够高效、准确地从一个地方传输到另一个地方。那么,究竟什么是BGP…...
苍穹外卖-day04:项目实战-套餐管理(新增套餐,分页查询套餐,删除套餐,修改套餐,起售停售套餐)业务类似于菜品模块
苍穹外卖-day04 课程内容 新增套餐套餐分页查询删除套餐修改套餐起售停售套餐 要求: 根据产品原型进行需求分析,分析出业务规则设计接口梳理表之间的关系(分类表、菜品表、套餐表、口味表、套餐菜品关系表)根据接口设计进行代…...
深入探索C与C++的混合编程
实现混合编程的技术细节 混合使用C和C可能由多种原因驱动。一方面,现有的大量优秀C语言库为特定任务提供了高效的解决方案,将这些库直接应用于C项目中可以节省大量的开发时间和成本。另一方面,C的高级特性如类、模板和异常处理等,…...
数组中的flat方法如何实现
数组的成员有时还是数组,Array.prototype.flat()用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。 [1, 2, [3, 4]].flat() // [1, 2, 3, 4]那flat怎么来实现呢? 1、使用while循环 实现的代码…...
哈尔滨发布最新公告/路由器优化大师
www.jn.zk.netZK开源Ajax Mobile Java框架的5.0.5版现已发布。 这是一个维护版本,具有针对ZK组件的改进功能和错误修复。 它将客户端引擎升级到jQuery 1.4.3版,并增强了列表框,树和布局组件。 5.0.5版带有一个新的LabelLocator接口ÿ…...
怎么使用织梦做网站/网站seo完整seo优化方案
场景:根据方法上的注解,通过java反射方式找到需要执行的的方法。1.注解类/**注解作用在方法上*/Target({ElementType.METHOD})/**注解的生命周期一直程序运行时都存在VM运行期间保留注解,可以通过反射机制读取注解信息*/Retention(RetentionPolicy.RUNTIME)/**注解包…...
全网展示型网站建设/优化关键词排名软件
未来的模样作文2000字未来的模样毫无悬念,成成首先跑到了家门口,着急的等待着我为他开门。见我和微微跟上来,他不停的跺跺脚,踢踢门,嘴里还欢快的喊着:“我又赢了!我又赢了!”边喊还…...
wordpress 禁止自动保存 插件/seo排名快速刷
学校的校园网每次重开电脑时都要重新打开浏览器进行网页登录,繁琐的操作比较麻烦,于是便写了个python的脚本进行自动登录,下面说下具体的操作过程:1. 方法说明博主采用的python的 requests库,发送post请求给登录网页&a…...
成都世迅网站建设/营销咨询服务
SwiftUI之明暗度brightness设置View Image Color brightness修饰器可以对视图进行明暗设置,我们可以从0%至100%设调节视图亮点。 本文价值与收获 看完本文后,您将能够作出下面的界面 Jietu20200328-1040352x.jpg看完本文您将掌握…...
多语言网站如何做/刚出来的新产品怎么推
根据他人的博客,可以发现pip也是可以离线安装已经下载好的包的,具体请参考《pip常用命令》。由于现在还没进展到那个程度,所以本次博客先记录一下我已经学会的东西。1、pip基本用法(1)以下载numpy为例,看如何使用pip下载第三方包只…...