异步转同步

业务需求

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

正常处理逻辑

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

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

如何转换为同步

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

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

思路

发起异步操作

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

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

循环等待

LoopQuery.java

使用 query(),将异步的操作 remoteCallback() 执行完成后,同步返回。

public class LoopQuery implements Async {

private String result;

private static final Logger LOGGER = LogManager.getLogger(LoopQuery.class.getName());

@Override

public String query(String key) {

startQuery(key);

new Thread(new Runnable() {

@Override

public 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() {

@Override

public 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 {

@Autowired

private ApplicationContext context;

private volatile BookingCreatedEvent bookingCreatedEvent;

/**

* 异步转同步查询

* @param info

* @return

*/

public String asyncQuery(final String info) {

query(info);

new Thread(new Runnable() {

@Override

public 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;

}

@EventListener

public 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);

//触发event

this.context.publishEvent(bookingCreatedEvent);

}

}

测试方法

@RunWith(SpringJUnit4Cla***unner.class)

@ContextConfiguration(classes = SpringConfig.class)

public class BookServiceTest {

@Autowired

private BookingService bookingService;

@Test

public 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 泛型

* @return 结果。如果超时则抛出异常

*/

public 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;

}

代码地址

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

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

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

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

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

  3. java服务端异步处理机制_Java异步处理机制实例详解

    通常同步意味着一个任务的某个处理过程会对多个线程在用串行化处理,而异步则意味着某个处理过程可以允许多个线程同时处理.下面我们就来看看有关异步处理的详细内容. 异步通常代表着更好的性能,因为它很大程度上 ...

  4. java 异步 返回值_java 异步

    注:本文基于 jdk1.8 1. 异步不需要返回值: CompletableFuture.runAsync() 示例代码如下: public JsonResult test() { JsonResul ...

  5. java 异步调用方法_java异步调用方法有哪些?如何实现异步调用?

    你知道java异步调用方法都有哪些吗?下面的文章内容,就对这方面的问题做了一下整理,一起来看看java异步调用的方法吧! 1.利用Spring的异步方法去执行 注:没有返回值 在启动类又或者是配置类加 ...

  6. java 异步线程池_Java - 异步线程池

    一.异步线程启动: new Thread newThread(newRunnable() { @Overridepublic voidrun() {//-- 这里是异步线程内的逻辑 } } ).sta ...

  7. java文件异步上传_java 异步上传文件

    我们的java上传文件,需要form同步上传,并且需要设置enctype为multipart/form-data. 如果将form使用ajax异步提交的话,将会报错说enctype不是multipar ...

  8. java 异步监听_java异步处理与监听器

    计算机的内存是有限的.tomcat 7 中,最多的线程为200,.为了最大化,需要异步,这样可以节省线程.具体什么是异步,为什么要用异步,我不想多写了,会好累的,我怕写着写着就不想继续下去了. 异步有 ...

  9. java 异步邮件发送_java异步发送邮件

    其中textTemplate和textTemplate2是spring注入的. package com.ucenter.service; import java.util.concurrent.Exe ...

最新文章

  1. “ u”到底是做什么的? “ git push -u原始主机”与“ git push原始主机”
  2. python join字符连接函数的使用方法
  3. 云上高并发系统改造最佳实践
  4. 拖延的本质是逃避!| 今日最佳
  5. matlab 符号表达式 系数 小数,matlab符号表达式系数
  6. 为企业提供本地销售人员的Universal Avenue获1000万美元A轮融资
  7. 【NOIP2001】【Luogu1027】Car的旅行路线
  8. Sharepoint学习笔记—ECM系列--从.CSV文件导入术语集(Term Sets)
  9. SQLyog 安装教程
  10. 怎样提高文章原创度,被快速收录?
  11. 9针串口的RS232、RS485、RS422引脚定义
  12. linux steam大屏幕模式,Steam 大屏幕模式 - Steam Support
  13. 2008服务器系统开机用户名和密码忘记了,服务器2008密码忘记了
  14. android paint 线宽_Android绘图:绘制直线的 drawLine方法
  15. 中南大学计算机复试分数线,2019年中南大学考研复试分数线
  16. 智能驾驶是什么意思_新手们的“必备”功能!解读DiPilot智能驾驶辅助系统
  17. android实现全国公祭日灰白模式
  18. Springboot考研网上辅导系统fu1ei计算机毕业设计-课程设计-期末作业-毕设程序代做
  19. 什么是GB18030,与GBK的关系?
  20. 程序员职场小白修炼记1——安晓辉《解忧程序员》读书笔记

热门文章

  1. mysql 无论输入什么都是现实 not found_Java高频面试题及答案
  2. 获取固件加载基地址的几种方法
  3. django models索引_Django开发者常犯的7种错误
  4. python的所有数据类型都可以相互转化吗_Python中如何进行数据类型转换?
  5. linux程序加载器,Linux 动态连接加载器 ld-linux用法
  6. python中的json_简单介绍Python中的JSON使用
  7. 图数据库应用系列(一):金融智能风控
  8. 电脑中没有oracle服务器,用AnySQL在没有oracle客户端的服务器上发送邮件
  9. php上传多张图片为什么只显示一张,javascript,_js多张图片上传 也拿到多张图片的路径 在页面上展示只显示一张?只执行了一次???,javascript - phpStudy...
  10. java 乐观锁 实例_JAVA乐观锁实现-CAS(示例代码)