java 异步查询转同步多种实现方式:循环等待,CountDownLatch,Spring EventListener,超时处理和空循环性能优化...
异步转同步
业务需求
有些接口查询反馈结果是异步返回的,无法立刻获取查询结果。
- 正常处理逻辑
触发异步操作,然后传递一个唯一标识。
等到异步结果返回,根据传入的唯一标识,匹配此次结果。
- 如何转换为同步
正常的应用场景很多,但是有时候不想做数据存储,只是想简单获取调用结果。
即想达到同步操作的结果,怎么办呢?
思路
发起异步操作
在异步结果返回之前,一直等待(可以设置超时)
结果返回之后,异步操作结果统一返回
循环等待
- LoopQuery.java
使用 query()
,将异步的操作 remoteCallback()
执行完成后,同步返回。
public class LoopQuery implements Async {private String result;private static final Logger LOGGER = LogManager.getLogger(LoopQuery.class.getName());@Overridepublic String query(String key) {startQuery(key);new Thread(new Runnable() {@Overridepublic void run() {remoteCallback(key);}}).start();final String queryResult = endQuery();LOGGER.info("查询结果: {}", queryResult);return queryResult;}/*** 开始查询* @param key 查询条件*/private void startQuery(final String key) {LOGGER.info("执行查询: {}", key);}/*** 远程的回调是等待是随机的** @param key 查询条件*/private void remoteCallback(final String key) {try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}this.result = key + "-result";LOGGER.info("remoteCallback set result: {}", result);}/*** 结束查询* @return 返回结果*/private String endQuery() {while (true) {if (null == result) {try {TimeUnit.MILLISECONDS.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}} else {return result;}}}
}
- main()
public static void main(String[] args) {new LoopQuery().query("12345");
}
- 测试结果
18:14:16.491 [main] INFO com.github.houbb.thread.learn.aysnc.loop.LoopQuery - 执行查询: 12345
18:14:21.498 [Thread-1] INFO com.github.houbb.thread.learn.aysnc.loop.LoopQuery - remoteCallback set result: 12345-result
18:14:21.548 [main] INFO com.github.houbb.thread.learn.aysnc.loop.LoopQuery - 查询结果: 12345-result
CountDownLatch
- AsyncQuery.java
使用 CountDownLatch
类达到同步的效果。
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;public class AsyncQuery {private static final Logger LOGGER = LogManager.getLogger(AsyncQuery.class.getName());/*** 结果*/private String result;/*** 异步转同步查询* @param key*/public void asyncQuery(final String key) {final CountDownLatch latch = new CountDownLatch(1);this.startQuery(key);new Thread(new Runnable() {@Overridepublic void run() {LOGGER.info("远程回调线程开始");remoteCallback(key, latch);LOGGER.info("远程回调线程结束");}}).start();try {latch.await();} catch (InterruptedException e) {e.printStackTrace();}this.endQuery();}private void startQuery(final String key) {LOGGER.info("执行查询: {}", key);}/*** 远程的回调是等待是随机的* @param key*/private void remoteCallback(final String key, CountDownLatch latch) {try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}this.result = key + "-result";latch.countDown();}private void endQuery() {LOGGER.info("查询结果: {}", result);}}
- main()
public static void main(String[] args) {AsyncQuery asyncQuery = new AsyncQuery();final String key = "123456";asyncQuery.asyncQuery(key);
}
- 日志
18:19:12.714 [main] INFO com.github.houbb.thread.learn.aysnc.countdownlatch.AsyncQuery - 执行查询: 123456
18:19:12.716 [Thread-1] INFO com.github.houbb.thread.learn.aysnc.countdownlatch.AsyncQuery - 远程回调线程开始
18:19:17.720 [main] INFO com.github.houbb.thread.learn.aysnc.countdownlatch.AsyncQuery - 查询结果: 123456-result
18:19:17.720 [Thread-1] INFO com.github.houbb.thread.learn.aysnc.countdownlatch.AsyncQuery - 远程回调线程结束
Spring EventListener
使用观察者模式也可以。(对方案一的优化)
此处结合 spring 进行使用。
- BookingCreatedEvent.java
定义一个传输属性的对象。
public class BookingCreatedEvent extends ApplicationEvent {private static final long serialVersionUID = -1387078212317348344L;private String info;public BookingCreatedEvent(Object source) {super(source);}public BookingCreatedEvent(Object source, String info) {super(source);this.info = info;}public String getInfo() {return info;}
}
- BookingService.java
说明:当 this.context.publishEvent(bookingCreatedEvent);
触发时,
会被 @EventListener
指定的方法监听到。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class BookingService {@Autowiredprivate ApplicationContext context;private volatile BookingCreatedEvent bookingCreatedEvent;/*** 异步转同步查询* @param info* @return*/public String asyncQuery(final String info) {query(info);new Thread(new Runnable() {@Overridepublic void run() {remoteCallback(info);}}).start();while(bookingCreatedEvent == null) {//.. 空循环// 短暂等待。try {TimeUnit.MILLISECONDS.sleep(1);} catch (InterruptedException e) {//...}//2. 使用两个单独的 event...}final String result = bookingCreatedEvent.getInfo();bookingCreatedEvent = null;return result;}@EventListenerpublic void onApplicationEvent(BookingCreatedEvent bookingCreatedEvent) {System.out.println("监听到远程的信息: " + bookingCreatedEvent.getInfo());this.bookingCreatedEvent = bookingCreatedEvent;System.out.println("监听到远程消息后: " + this.bookingCreatedEvent.getInfo());}/*** 执行查询* @param info*/public void query(final String info) {System.out.println("开始查询: " + info);}/*** 远程回调* @param info*/public void remoteCallback(final String info) {System.out.println("远程回调开始: " + info);try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}// 重发结果事件String result = info + "-result";BookingCreatedEvent bookingCreatedEvent = new BookingCreatedEvent(this, result);//触发eventthis.context.publishEvent(bookingCreatedEvent);}
}
- 测试方法
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class BookServiceTest {@Autowiredprivate BookingService bookingService;@Testpublic void asyncQueryTest() {bookingService.asyncQuery("1234");}}
- 日志
2018-08-10 18:27:05.958 INFO [main] com.github.houbb.spring.lean.core.ioc.event.BookingService:84 - 开始查询:1234
2018-08-10 18:27:05.959 INFO [Thread-2] com.github.houbb.spring.lean.core.ioc.event.BookingService:93 - 远程回调开始:1234
接收到信息: 1234-result
2018-08-10 18:27:07.964 INFO [Thread-2] com.github.houbb.spring.lean.core.ioc.event.BookingService:73 - 监听到远程的信息: 1234-result
2018-08-10 18:27:07.964 INFO [Thread-2] com.github.houbb.spring.lean.core.ioc.event.BookingService:75 - 监听到远程消息后: 1234-result
2018-08-10 18:27:07.964 INFO [Thread-2] com.github.houbb.spring.lean.core.ioc.event.BookingService:106 - 已经触发event
2018-08-10 18:27:07.964 INFO [main] com.github.houbb.spring.lean.core.ioc.event.BookingService:67 - 查询结果: 1234-result
2018-08-10 18:27:07.968 INFO [Thread-1] org.springframework.context.support.GenericApplicationContext:993 - Closing org.springframework.context.support.GenericApplicationContext@5cee5251: startup date [Fri Aug 10 18:27:05 CST 2018]; root of context hierarchy
超时和空循环
空循环
空循环会导致 cpu 飙升
while(true) {
}
- 解决方式
while(true) {// 小睡即可TimeUnit.sleep(1);
}
超时编写
不可能一直等待反馈,可以设置超时时间。
/*** 循环等待直到获取结果* @param key key* @param timeoutInSeconds 超时时间* @param <T> 泛型* @return 结果。如果超时则抛出异常*/
public <T> T loopWaitForValue(final String key, long timeoutInSeconds) {long startTime = System.nanoTime();long deadline = startTime + TimeUnit.SECONDS.toNanos(timeoutInSeconds);//1. 如果没有新回调,或者 key 对应元素不存在。则一直循环while(ObjectUtil.isNull(map.get(key))) {try {TimeUnit.MILLISECONDS.sleep(5);} catch (InterruptedException e) {LOGGER.warn("Loop meet InterruptedException, just ignore it.", e);}// 超时判断long currentTime = System.nanoTime();if(currentTime >= deadline) {throw new BussinessException(ErrorCode.READ_TIME_OUT);}}final T target = (T) map.get(key);LOGGER.debug("loopWaitForValue get value:{} for key:{}", JSON.toJSON(target), key);//2. 获取到元素之后,需要移除掉对应的值map.remove(key);return target;
}
代码地址
loop
countdownlatch
spring-event-listener
转载于:https://www.cnblogs.com/houbbBlogs/p/9458102.html
java 异步查询转同步多种实现方式:循环等待,CountDownLatch,Spring EventListener,超时处理和空循环性能优化...相关推荐
- java异步处理同步化_java 异步查询转同步多种实现方式:循环等待,CountDownLatch,Spring EventListener,超时处理和空循环性能优化...
异步转同步 业务需求 有些接口查询反馈结果是异步返回的,无法立刻获取查询结果. 正常处理逻辑 触发异步操作,然后传递一个唯一标识. 等到异步结果返回,根据传入的唯一标识,匹配此次结果. 如何转换为同步 ...
- java异步处理同步化_java 异步查询转同步多种实现方式:循环等待,CountDownLatch,Spring Even...
异步转同步 业务需求 有些接口查询反馈结果是异步返回的,无法立刻获取查询结果. 正常处理逻辑 触发异步操作,然后传递一个唯一标识. 等到异步结果返回,根据传入的唯一标识,匹配此次结果. 如何转换为同步 ...
- mysql异步查询 java_java 手写并发框架(一)异步查询转同步的 7 种实现方式
序言 本节将学习一下如何实现异步查询转同步的方式,共计介绍了 7 种常见的实现方式. 思维导图如下: 异步转同步 业务需求 有些接口查询反馈结果是异步返回的,无法立刻获取查询结果. 比如业务开发中我们 ...
- 并发查询_java 手写并发框架(一)异步查询转同步的7种实现方式
序言 本节将学习一下如何实现异步查询转同步的方式,共计介绍了 7 种常见的实现方式. 思维导图如下: 思维导图 异步转同步 业务需求 有些接口查询反馈结果是异步返回的,无法立刻获取查询结果. 比如业务 ...
- java异步接口转同步接口_如果今天设计了Java:同步接口
java异步接口转同步接口 Java已经走了很长一段路. 很长的路要走. 它带有早期设计决策中的所有"垃圾". 一遍又一遍后悔的一件事是, 每个对象(可能)都包含一个监视器 . 几 ...
- java异步日志跟同步区别_AJAX中同步和异步的区别和使用场景
AJAX中根据async的值不同分为同步(async = false)和异步(async = true)两种执行方式:在W3C的教程中推荐使用异步执行: 下面来区别一下同步和异步有什么不同: 异步:在 ...
- 复位电路设计(异步复位、同步释放)
以下内容摘自正点原子的:<逻辑设计开发指南> 复位电路是数字逻辑设计中非常常用的电路,不管是 FPGA 还是 ASIC 设计,都会涉及到复位,一般 FPGA或者 ASIC 的复位需要我们自 ...
- Java 异步编程 (5 种异步实现方式详解)
同步操作如果遇到一个耗时的方法,需要阻塞等待,那么我们有没有办法解决呢?让它异步执行,下面我会详解异步及实现 @mikechen 目录 什么是异步? 一.线程异步 二.Future异步 三.Compl ...
- Java异步非阻塞编程的几种方式
简介: Java异步非阻塞编程的几种方式 一. 从一个同步的Http调用说起 一个很简单的业务逻辑,其他后端服务提供了一个接口,我们需要通过接口调用,获取到响应的数据. 逆地理接口:通过经纬度获取这个 ...
最新文章
- 鸿蒙系统3.0演示,华为鸿蒙系统3.0-华为鸿蒙系统3.0官网申请地址预约 v1.0-优盘手机站...
- GDAL库简介以及在Windows下编译过程
- 目前154万AI开发者
- Wire:Linux开源聊天应用
- python 爬带端口的网站_程序员带你爬取爬虫最爱扒的网站数据。快来看!
- java poi 操作ppt
- 如何手动删除并重新安装 .NET Framework 2.0
- vc只能调用matlab子函数,Vc++6.0调用matlab的数学库函数
- 黑客攻防技术宝典web实战篇:工具web服务器习题
- stdio.h库函数
- 我的2016——程序员年到三十,工作第四年
- 用中文把玩Google开源的Deep-Learning项目word2vec
- Geant4能谱展宽【Gaussian Broadning】-root作图
- 嵌入式Linux引导过程之1.4——Xloader的ddr_init
- [译] 用于 iOS 的 ML Kit 教程:识别图像中的文字
- 灰度方案-svc环境实现方案以及网关源码分析
- Ubuntu 15.04 安装 Nvidia Quadro系列显卡驱动
- 2020回顾,2021学习目标
- 电力英语和计算机考试难吗,四六级、计算机这些证书真的影响进电网吗?
- jquery ajax请求封装 (promise)