多种实现异步编程的方法

文章目录

  • 一、线程Thread
  • 二、Future异步
  • 三、 CompletableFuture实现异步
  • 四、Spring的@Async异步
  • 五、Spring ApplicationEvent事件实现异步
  • 六、消息队列
  • 七、ThreadUtil异步工具类
  • 八、Guava异步


一、线程Thread

public class AsyncThread extends Thread {@Overridepublic void run() {//下面是要处理的业务System.out.println("Current thread name:" + Thread.currentThread().getName() + " Send email success!");}public static void main(String[] args) {AsyncThread asyncThread = new AsyncThread();asyncThread.run();}
}

当然如果每次都创建一个Thread线程,频繁的创建、销毁,浪费系统资源,我们可以采用线程池:
可以参考之前的文章:
1.手动创建线程池:ThreadPoolExecutor手动创建线程池优于Executors类
2.如何手动创建线程池:使用ThreadPoolExecutor手动创建线程池+线程池工具类+使用线程池

这里先简单创建一个线程池提供参考(阿里开发手册不提倡此方法创建),可以将业务逻辑封装到Runnable或Callable中,交由线程池来执行

private ExecutorService executorService = Executors.newCachedThreadPool();public void fun() {executorService.submit(new Runnable() {@Overridepublic void run() {log.info("执行业务逻辑...");}});
}

二、Future异步

@Slf4j
public class FutureManager {public String execute() throws Exception {ExecutorService executor = Executors.newFixedThreadPool(1);Future<String> future = executor.submit(new Callable<String>() {@Overridepublic String call() throws Exception {System.out.println(" --- task start --- ");Thread.sleep(3000);System.out.println(" --- task finish ---");return "this is future execute final result!!!";}});//这里需要返回值时会阻塞主线程String result = future.get();log.info("Future get result: {}", result);return result;}@SneakyThrowspublic static void main(String[] args) {FutureManager manager = new FutureManager();manager.execute();}
}

Future的不足之处的包括以下几点:

1️⃣无法被动接收异步任务的计算结果:虽然我们可以主动将异步任务提交给线程池中的线程来执行,但是待异步任务执行结束之后,主线程无法得到任务完成与否的通知,它需要通过get方法主动获取任务执行的结果。

2️⃣ Future件彼此孤立:有时某一个耗时很长的异步任务执行结束之后,你想利用它返回的结果再做进一步的运算,该运算也会是一个异步任务,两者之间的关系需要程序开发人员手动进行绑定赋予,Future并不能将其形成一个任务流(pipeline),每一个Future都是彼此之间都是孤立的,所以才有了后面的CompletableFuture,CompletableFuture就可以将多个Future串联起来形成任务流。

3️⃣ Futrue没有很好的错误处理机制:截止目前,如果某个异步任务在执行发的过程中发生了异常,调用者无法被动感知,必须通过捕获get方法的异常才知晓异步任务执行是否出现了错误,从而在做进一步的判断处理。

三、 CompletableFuture实现异步

public class CompletableFutureCompose {/*** thenAccept子任务和父任务公用同一个线程*/@SneakyThrowspublic static void thenRunAsync() {CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread() + " cf1 do something....");return 1;});CompletableFuture<Void> cf2 = cf1.thenRunAsync(() -> {System.out.println(Thread.currentThread() + " cf2 do something...");});//等待任务1执行完成System.out.println("cf1结果->" + cf1.get());//等待任务2执行完成System.out.println("cf2结果->" + cf2.get());}public static void main(String[] args) {thenRunAsync();}
}

我们不需要显式使用ExecutorService,CompletableFuture
内部使用了ForkJoinPool来处理异步任务,如果在某些业务场景我们想自定义自己的异步线程池也是可以的。

四、Spring的@Async异步

在实际项目中, 使用@Async调用线程池,推荐的方式是使用自定义线程池的模式,不推荐直接使用@Async直接实现异步。

1.自定义线程池

/*** 线程池参数配置,多个线程池实现线程池隔离,@Async注解,默认使用系统自定义线程池,可在项目中设置多个线程池,在异步调用的时候,指明需要调用的线程池名称,比如:@Async("taskName")** @author: 晃科比过乔丹* @since: 2024/04/13 11:44**/
@EnableAsync
@Configuration
public class TaskPoolConfig {/*** 自定义线程池** @author: jacklin* @since: 2021/11/16 17:41**/@Bean("taskExecutor")public Executor taskExecutor() {//返回可用处理器的Java虚拟机的数量 12int i = Runtime.getRuntime().availableProcessors();System.out.println("系统最大线程数  : " + i);ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//核心线程池大小executor.setCorePoolSize(16);//最大线程数executor.setMaxPoolSize(20);//配置队列容量,默认值为Integer.MAX_VALUEexecutor.setQueueCapacity(99999);//活跃时间executor.setKeepAliveSeconds(60);//线程名字前缀executor.setThreadNamePrefix("asyncServiceExecutor -");//设置此执行程序应该在关闭时阻止的最大秒数,以便在容器的其余部分继续关闭之前等待剩余的任务完成他们的执行executor.setAwaitTerminationSeconds(60);//等待所有的任务结束后再关闭线程池executor.setWaitForTasksToCompleteOnShutdown(true);return executor;}
}

2.实现异步

public interface AsyncService {MessageResult sendSms(String callPrefix, String mobile, String actionType, String content);MessageResult sendEmail(String email, String subject, String content);
}@Slf4j
@Service
public class AsyncServiceImpl implements AsyncService {@Autowiredprivate IMessageHandler mesageHandler;@Override@Async("taskExecutor")public MessageResult sendSms(String callPrefix, String mobile, String actionType, String content) {try {Thread.sleep(1000);mesageHandler.sendSms(callPrefix, mobile, actionType, content);} catch (Exception e) {log.error("发送短信异常 -> ", e)}}@Override@Async("taskExecutor")public sendEmail(String email, String subject, String content) {try {Thread.sleep(1000);mesageHandler.sendsendEmail(email, subject, content);} catch (Exception e) {log.error("发送email异常 -> ", e)}}
}

五、Spring ApplicationEvent事件实现异步

具体使用可以参考之前的文章:事件机制
补充说明:可能有些时候采用ApplicationEvent实现异步的使用,当程序出现异常错误的时候,需要考虑补偿机制,那么这时候可以结合Spring Retry重试来帮助我们避免这种异常造成数据不一致问题。

六、消息队列

1.回调事件消息生产者

@Slf4j
@Component
public class CallbackProducer {@AutowiredAmqpTemplate amqpTemplate;public void sendCallbackMessage(CallbackDTO allbackDTO, final long delayTimes) {log.info("生产者发送消息,callbackDTO,{}", callbackDTO);amqpTemplate.convertAndSend(CallbackQueueEnum.QUEUE_GENSEE_CALLBACK.getExchange(), CallbackQueueEnum.QUEUE_GENSEE_CALLBACK.getRoutingKey(), JsonMapper.getInstance().toJson(genseeCallbackDTO), new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws AmqpException {//给消息设置延迟毫秒值,通过给消息设置x-delay头来设置消息从交换机发送到队列的延迟时间message.getMessageProperties().setHeader("x-delay", delayTimes);message.getMessageProperties().setCorrelationId(callbackDTO.getSdkId());return message;}});}
}

2.回调事件消息消费者

@Slf4j
@Component
@RabbitListener(queues = "message.callback", containerFactory = "rabbitListenerContainerFactory")
public class CallbackConsumer {@Autowiredprivate IGlobalUserService globalUserService;@RabbitHandlerpublic void handle(String json, Channel channel, @Headers Map<String, Object> map) throws Exception {if (map.get("error") != null) {//否认消息channel.basicNack((Long) map.get(AmqpHeaders.DELIVERY_TAG), false, true);return;}try {CallbackDTO callbackDTO = JsonMapper.getInstance().fromJson(json, CallbackDTO.class);//执行业务逻辑globalUserService.execute(callbackDTO);//消息消息成功手动确认,对应消息确认模式acknowledge-mode: manualchannel.basicAck((Long) map.get(AmqpHeaders.DELIVERY_TAG), false);} catch (Exception e) {log.error("回调失败 -> {}", e);}}
}

七、ThreadUtil异步工具类

@Slf4j
public class ThreadUtils {public static void main(String[] args) {for (int i = 0; i < 3; i++) {ThreadUtil.execAsync(() -> {ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();int number = threadLocalRandom.nextInt(20) + 1;System.out.println(number);});log.info("当前第:" + i + "个线程");}log.info("task finish!");}
}

八、Guava异步

Guava的ListenableFuture顾名思义就是可以监听的Future,是对java原生Future的扩展增强。我们知道Future表示一个异步计算任务,当任务完成时可以得到计算结果。

如果我们希望一旦计算完成就拿到结果展示给用户或者做另外的计算,就必须使用另一个线程不断的查询计算状态。这样做,代码复杂,而且效率低下。

使用Guava ListenableFuture可以帮我们检测Future是否完成了,不需要再通过get()方法苦苦等待异步的计算结果,如果完成就自动调用回调函数,这样可以减少并发程序的复杂度。

ListenableFuture是一个接口,它从jdk的Future接口继承,添加了void addListener(Runnable listener, Executor executor)方法。

1.定义ListenableFuture的实例:

ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());final ListenableFuture<Integer> listenableFuture = executorService.submit(new Callable<Integer>() {@Overridepublic Integer call() throws Exception {log.info("callable execute...")TimeUnit.SECONDS.sleep(1);return 1;}});

首先通过MoreExecutors类的静态方法listeningDecorator方法初始化一个ListeningExecutorService的方法,然后使用此实例的submit方法即可初始化ListenableFuture对象。

ListenableFuture要做的工作,在Callable接口的实现类中定义,这里只是休眠了1秒钟然后返回一个数字1,有了ListenableFuture实例,可以执行此Future并执行Future完成之后的回调函数。

 Futures.addCallback(listenableFuture, new FutureCallback<Integer>() {@Overridepublic void onSuccess(Integer result) {//成功执行...System.out.println("Get listenable future's result with callback " + result);}@Overridepublic void onFailure(Throwable t) {//异常情况处理...t.printStackTrace();}
});

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

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

相关文章

Day19-【Java SE进阶】网络编程

一、网络编程 1.概述 可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信的)。java.net,*包下提供了网络编程的解决方案! 基本的通信架构 基本的通信架构有2种形式:CS架构(Client客户端/Server服务端)、BS架构(Browser浏览器/Server服务端)。 网络通信的…

拓展企业发展新天地:低代码开发平台的策略价值

一、什么是低代码开发&#xff1f; 低代码开发平台&#xff1a;一个号称在几分钟内就能构建出一套企业内部适用的系统开发工具。低代码开发平台可以从降低开发者技术门槛、减少企业人力成本、协同共享资源、灵活搭建组件、减少bug率、按需收费等多个方面降低企业开发成本。 曾…

MVC设计模式的思想

MVC模式(Model-View-Controller) 是一种软件设计模式&#xff0c;它将应用程序分为三个部分&#xff1a;模型、视图和控制器。这个模式的目的是将应用程序的表示&#xff08;视图&#xff09;与处理&#xff08;控制器&#xff09;分开&#xff0c;以及将应用程序的数据和业务逻…

排序1——C语言

排序 1. 复杂度2. 插入排序2.1 直接插入排序2.2 希尔排序 3. 选择排序3.1 直接选择排序3.2 堆排序 排序在生活中很常见&#xff0c;比如在网购时&#xff0c;按价格排序&#xff0c;按好评数排序&#xff0c;点餐时&#xff0c;按评分排序等等。而排序有快和慢&#xff0c;快的…

洛谷 P3834 可持久化线段树 2 题解

【模板】可持久化线段树 2 题目描述 如题&#xff0c;给定 n n n 个整数构成的序列 a a a&#xff0c;将对于指定的闭区间 [ l , r ] [l, r] [l,r] 查询其区间内的第 k k k 小值。 输入格式 第一行包含两个整数&#xff0c;分别表示序列的长度 n n n 和查询的个数 m …

【磁盘清理】/var/lib/docker/overlay2 占用空间过大

找到对应的容器 1.查看目前的容器占用的空间(可略过) https://blog.csdn.net/weixin_43944305/article/details/106152976 进行简单的清理 docker system df docker system df -v docker system prune2. 查看overlay2的占用空间&#xff0c; 找到对应的容器 https://blog.c…

Navicat Premium 16 for Mac/Win:数据库管理的全能之选

在数字化时代&#xff0c;数据库管理已成为各行各业不可或缺的一环。而Navicat Premium 16作为一款功能强大的数据库管理软件&#xff0c;无疑为数据库管理员和开发者提供了高效、便捷的解决方案。 Navicat Premium 16支持多种主流数据库系统&#xff0c;无论是MySQL、Postgre…

编程新手必看,Python3编程第一步语句学习(15)

介绍&#xff1a; 在开始编写Python 3程序之前&#xff0c;有一些基本步骤和概念需要了解。以下是开始Python 3编程之旅的第一步介绍&#xff1a; 安装Python&#xff1a; 访问Python官方网站 python.org 下载适合您操作系统的Python版本&#xff08;确保选择Python 3而非Pytho…

硕士毕业论文评审老师的评审标准

硕士毕业论文评审老师的评审标准通常涉及以下几个方面&#xff1a; 工作态度与努力&#xff1a;评审老师会考察学生在毕业论文工作期间是否表现出刻苦努力、态度认真的精神&#xff0c;并且是否遵守了各项相关纪律。 任务完成情况&#xff1a;学生是否能按时、全面、独立地完成…

JS - BOM(浏览器对象模型)

BOM 浏览器对象模型 BOM可以使我们通过JS来操作浏览器 在BOM中为我们提供了一组对象&#xff0c;用来完成对浏览器的操作 BOM对象 BOM&#xff08;Browser Object Model&#xff09;是指浏览器对象模型&#xff0c;它提供了与浏览器窗口进行交互的对象和方法。BOM包括一些核…

Linux系统——Zookeeper集群

目录 一、Zookeeper概述 1.Zookeeper简介 2.Zookeeper工作机制 3.Zookeeper数据结构 4.Zookeeper应用场景 4.1统一命名服务 4.2统一配置管理 4.3统一集群管理 4.4服务器动态上下线 4.5软负载均衡 5.Zookeeper选举机制 5.1第一次启动选举机制 5.2非第一次启动选举机…

【Unity】常见性能优化

1 前言 本文将介绍下常用的Unity自带的常用优化工具&#xff0c;并介绍部分常用优化方法。都是比较基础的内容。 2 界面 2.1 Statistics窗口 可以简单查看Unity运行时的统计数据&#xff0c;当前一帧的性能数据。 2.1.1 Audio 音频相关内容。 Level&#xff1a;音量大小&a…

图机器学习导论

图&#xff1a;描述关系数据的通用语言&#xff0c;起源于哥尼斯堡七桥问题 传统的机器学习&#xff1a;数据样本之间独立同分布&#xff0c;简单拟合数据边界&#xff0c;在传统的机器学习中&#xff0c;每个数据样本彼此无关。传统的神经网络&#xff0c;只能处理简单的表格、…

实体行业的品牌传播与网络运营,迅腾文化解决完善品牌定位运营

实体行业的品牌传播与网络运营&#xff0c;迅腾文化解决完善品牌定位运营 在今日的商业环境中&#xff0c;如何借助网络的力量&#xff0c;将品牌有效地传播出去&#xff0c;让渠道商、加盟商和消费者感受到安全感&#xff0c;成为了深思的问题。品牌是一个企业的灵魂&#xf…

《青少年成长管理2024》048 “成长目标:到哪儿了?”1/2

《青少年成长管理2024》048 “成长目标&#xff1a;到哪儿了&#xff1f;”1/2 一、神奇的地球二、群居的人类三、比较而存在四、竞争与合作五、等级和秩序 本节摘要&#xff1a;当你来到这个世界&#xff0c;首先认识一下这是一个什么样的世界&#xff0c;你处于一个什么样的环…

动态规划先导片

大家知道动规是由前一个状态推导出来的&#xff0c;而贪心是局部直接选最优的&#xff0c;对于刷题来说就够用了。 对于动态规划问题&#xff0c;我将拆解为如下五步曲&#xff0c;这五步都搞清楚了&#xff0c;才能说把动态规划真的掌握了&#xff01; 确定dp数组&#xff0…

linux造成只读模式的原因和解决方法

造成硬盘分区只读的可能原因有: 文件系统错误,内核相关硬件驱动bug,FW固件类问题,磁盘坏道 硬盘背板故障,硬盘线缆故障,HBA卡故障,RAID卡故障,随意开关机系统损坏 使用用 fsck – y /dev/sda (/dev/sda指你需要修复的分区) 来修复文件系统 mount -t vfat /dev/sda1…

开源!工厂数字化项目会用到的地理信息系统

软件介绍 QGIS&#xff08;Quantum GIS&#xff09;是一款免费、开源、跨平台的地理信息系统&#xff08;GIS&#xff09;软件&#xff0c;适用于Unix平台、Windows和MacOS。提供了强大且用户友好的功能&#xff0c;使其成为地理信息处理领域的热门选择。 功能特点 1.空间数据管…

react使用npm i @reduxjs/toolkit react-redux

npm i reduxjs/toolkit react-redux 创建一个 store文件夹&#xff0c;里面创建index.js文件和子模块文件夹 index,js文件写入以下代码 import {configureStore} from reduxjs/toolkit // 导入子模块 import counterReducer from ./modules/one import two from ./modules/tw…

鸿蒙TypeScript学习第14天:【联合类型】

1、TypeScript 联合类型 联合类型&#xff08;Union Types&#xff09;可以通过管道(|)将变量设置多种类型&#xff0c;赋值时可以根据设置的类型来赋值。 注意&#xff1a;只能赋值指定的类型&#xff0c;如果赋值其它类型就会报错。 创建联合类型的语法格式如下&#xff1…