异步转同步

业务需求

有些接口查询反馈结果是异步返回的,无法立刻获取查询结果。

  • 正常处理逻辑

触发异步操作,然后传递一个唯一标识。

等到异步结果返回,根据传入的唯一标识,匹配此次结果。

  • 如何转换为同步

正常的应用场景很多,但是有时候不想做数据存储,只是想简单获取调用结果。

即想达到同步操作的结果,怎么办呢?

思路

  1. 发起异步操作

  2. 在异步结果返回之前,一直等待(可以设置超时)

  3. 结果返回之后,异步操作结果统一返回

循环等待

  • 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,超时处理和空循环性能优化...相关推荐

  1. java异步处理同步化_java 异步查询转同步多种实现方式:循环等待,CountDownLatch,Spring EventListener,超时处理和空循环性能优化...

    异步转同步 业务需求 有些接口查询反馈结果是异步返回的,无法立刻获取查询结果. 正常处理逻辑 触发异步操作,然后传递一个唯一标识. 等到异步结果返回,根据传入的唯一标识,匹配此次结果. 如何转换为同步 ...

  2. java异步处理同步化_java 异步查询转同步多种实现方式:循环等待,CountDownLatch,Spring Even...

    异步转同步 业务需求 有些接口查询反馈结果是异步返回的,无法立刻获取查询结果. 正常处理逻辑 触发异步操作,然后传递一个唯一标识. 等到异步结果返回,根据传入的唯一标识,匹配此次结果. 如何转换为同步 ...

  3. mysql异步查询 java_java 手写并发框架(一)异步查询转同步的 7 种实现方式

    序言 本节将学习一下如何实现异步查询转同步的方式,共计介绍了 7 种常见的实现方式. 思维导图如下: 异步转同步 业务需求 有些接口查询反馈结果是异步返回的,无法立刻获取查询结果. 比如业务开发中我们 ...

  4. 并发查询_java 手写并发框架(一)异步查询转同步的7种实现方式

    序言 本节将学习一下如何实现异步查询转同步的方式,共计介绍了 7 种常见的实现方式. 思维导图如下: 思维导图 异步转同步 业务需求 有些接口查询反馈结果是异步返回的,无法立刻获取查询结果. 比如业务 ...

  5. java异步接口转同步接口_如果今天设计了Java:同步接口

    java异步接口转同步接口 Java已经走了很长一段路. 很长的路要走. 它带有早期设计决策中的所有"垃圾". 一遍又一遍后悔的一件事是, 每个对象(可能)都包含一个监视器 . 几 ...

  6. java异步日志跟同步区别_AJAX中同步和异步的区别和使用场景

    AJAX中根据async的值不同分为同步(async = false)和异步(async = true)两种执行方式:在W3C的教程中推荐使用异步执行: 下面来区别一下同步和异步有什么不同: 异步:在 ...

  7. 复位电路设计(异步复位、同步释放)

    以下内容摘自正点原子的:<逻辑设计开发指南> 复位电路是数字逻辑设计中非常常用的电路,不管是 FPGA 还是 ASIC 设计,都会涉及到复位,一般 FPGA或者 ASIC 的复位需要我们自 ...

  8. Java 异步编程 (5 种异步实现方式详解)

    同步操作如果遇到一个耗时的方法,需要阻塞等待,那么我们有没有办法解决呢?让它异步执行,下面我会详解异步及实现 @mikechen 目录 什么是异步? 一.线程异步 二.Future异步 三.Compl ...

  9. Java异步非阻塞编程的几种方式

    简介: Java异步非阻塞编程的几种方式 一. 从一个同步的Http调用说起 一个很简单的业务逻辑,其他后端服务提供了一个接口,我们需要通过接口调用,获取到响应的数据. 逆地理接口:通过经纬度获取这个 ...

最新文章

  1. 鸿蒙系统3.0演示,华为鸿蒙系统3.0-华为鸿蒙系统3.0官网申请地址预约 v1.0-优盘手机站...
  2. GDAL库简介以及在Windows下编译过程
  3. 目前154万AI开发者
  4. Wire:Linux开源聊天应用
  5. python 爬带端口的网站_程序员带你爬取爬虫最爱扒的网站数据。快来看!
  6. java poi 操作ppt
  7. 如何手动删除并重新安装 .NET Framework 2.0
  8. vc只能调用matlab子函数,Vc++6.0调用matlab的数学库函数
  9. 黑客攻防技术宝典web实战篇:工具web服务器习题
  10. stdio.h库函数
  11. 我的2016——程序员年到三十,工作第四年
  12. 用中文把玩Google开源的Deep-Learning项目word2vec
  13. Geant4能谱展宽【Gaussian Broadning】-root作图
  14. 嵌入式Linux引导过程之1.4——Xloader的ddr_init
  15. [译] 用于 iOS 的 ML Kit 教程:识别图像中的文字
  16. 灰度方案-svc环境实现方案以及网关源码分析
  17. Ubuntu 15.04 安装 Nvidia Quadro系列显卡驱动
  18. 2020回顾,2021学习目标
  19. 电力英语和计算机考试难吗,四六级、计算机这些证书真的影响进电网吗?
  20. jquery ajax请求封装 (promise)

热门文章

  1. android 接百度SDK遇到的坑(百度地图BD09经纬度转高德地图GCJ02经纬度)
  2. VMWare下窗口大小调整
  3. 阿里官宣AI框架大牛贾扬清加盟,任职技术VP
  4. 对不起,AI觉得你有虐童倾向,求职失败
  5. 何小鹏总结2017:小鹏汽车融资近50亿,上市车型下月亮相CES
  6. 景驰无人车总部落户广州:明年最低量产500辆,回应百度官司
  7. 报名 | 旷视研究院解读COCO2017人体姿态估计竞赛冠军论文
  8. 亚马逊发布新版MXNet:支持英伟达Volta和稀疏张量
  9. python+appium 自动化2--元素定位uiautomatorviewer
  10. 20160402javaweb 开发模式