摘要:異步轉(zhuǎn)同步業(yè)務(wù)需求有些接口查詢反饋結(jié)果是異步返回的,無(wú)法立刻獲取查詢結(jié)果。正常處理邏輯觸發(fā)異步操作,然后傳遞一個(gè)唯一標(biāo)識(shí)。等到異步結(jié)果返回,根據(jù)傳入的唯一標(biāo)識(shí),匹配此次結(jié)果。異步轉(zhuǎn)同步查詢空循環(huán)短暫等待。
異步轉(zhuǎn)同步 業(yè)務(wù)需求
有些接口查詢反饋結(jié)果是異步返回的,無(wú)法立刻獲取查詢結(jié)果。
正常處理邏輯
觸發(fā)異步操作,然后傳遞一個(gè)唯一標(biāo)識(shí)。
等到異步結(jié)果返回,根據(jù)傳入的唯一標(biāo)識(shí),匹配此次結(jié)果。
如何轉(zhuǎn)換為同步
正常的應(yīng)用場(chǎng)景很多,但是有時(shí)候不想做數(shù)據(jù)存儲(chǔ),只是想簡(jiǎn)單獲取調(diào)用結(jié)果。
即想達(dá)到同步操作的結(jié)果,怎么辦呢?
思路發(fā)起異步操作
在異步結(jié)果返回之前,一直等待(可以設(shè)置超時(shí))
結(jié)果返回之后,異步操作結(jié)果統(tǒng)一返回
循環(huán)等待LoopQuery.java
使用 query(),將異步的操作 remoteCallback() 執(zhí)行完成后,同步返回。
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("查詢結(jié)果: {}", queryResult); return queryResult; } /** * 開始查詢 * @param key 查詢條件 */ private void startQuery(final String key) { LOGGER.info("執(zhí)行查詢: {}", key); } /** * 遠(yuǎn)程的回調(diào)是等待是隨機(jī)的 * * @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); } /** * 結(jié)束查詢 * @return 返回結(jié)果 */ 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"); }
測(cè)試結(jié)果
18:14:16.491 [main] INFO com.github.houbb.thread.learn.aysnc.loop.LoopQuery - 執(zhí)行查詢: 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 - 查詢結(jié)果: 12345-resultCountDownLatch
AsyncQuery.java
使用 CountDownLatch 類達(dá)到同步的效果。
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()); /** * 結(jié)果 */ private String result; /** * 異步轉(zhuǎn)同步查詢 * @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("遠(yuǎn)程回調(diào)線程開始"); remoteCallback(key, latch); LOGGER.info("遠(yuǎn)程回調(diào)線程結(jié)束"); } }).start(); try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } this.endQuery(); } private void startQuery(final String key) { LOGGER.info("執(zhí)行查詢: {}", key); } /** * 遠(yuǎn)程的回調(diào)是等待是隨機(jī)的 * @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("查詢結(jié)果: {}", 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 - 執(zhí)行查詢: 123456 18:19:12.716 [Thread-1] INFO com.github.houbb.thread.learn.aysnc.countdownlatch.AsyncQuery - 遠(yuǎn)程回調(diào)線程開始 18:19:17.720 [main] INFO com.github.houbb.thread.learn.aysnc.countdownlatch.AsyncQuery - 查詢結(jié)果: 123456-result 18:19:17.720 [Thread-1] INFO com.github.houbb.thread.learn.aysnc.countdownlatch.AsyncQuery - 遠(yuǎn)程回調(diào)線程結(jié)束Spring EventListener
使用觀察者模式也可以。(對(duì)方案一的優(yōu)化)
此處結(jié)合 spring 進(jìn)行使用。
BookingCreatedEvent.java
定義一個(gè)傳輸屬性的對(duì)象。
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
說明:當(dāng) this.context.publishEvent(bookingCreatedEvent); 觸發(fā)時(shí),
會(huì)被 @EventListener 指定的方法監(jiān)聽到。
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; /** * 異步轉(zhuǎn)同步查詢 * @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) { //.. 空循環(huán) // 短暫等待。 try { TimeUnit.MILLISECONDS.sleep(1); } catch (InterruptedException e) { //... } //2. 使用兩個(gè)多帶帶的 event... } final String result = bookingCreatedEvent.getInfo(); bookingCreatedEvent = null; return result; } @EventListener public void onApplicationEvent(BookingCreatedEvent bookingCreatedEvent) { System.out.println("監(jiān)聽到遠(yuǎn)程的信息: " + bookingCreatedEvent.getInfo()); this.bookingCreatedEvent = bookingCreatedEvent; System.out.println("監(jiān)聽到遠(yuǎn)程消息后: " + this.bookingCreatedEvent.getInfo()); } /** * 執(zhí)行查詢 * @param info */ public void query(final String info) { System.out.println("開始查詢: " + info); } /** * 遠(yuǎn)程回調(diào) * @param info */ public void remoteCallback(final String info) { System.out.println("遠(yuǎn)程回調(diào)開始: " + info); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } // 重發(fā)結(jié)果事件 String result = info + "-result"; BookingCreatedEvent bookingCreatedEvent = new BookingCreatedEvent(this, result); //觸發(fā)event this.context.publishEvent(bookingCreatedEvent); } }
測(cè)試方法
@RunWith(SpringJUnit4ClassRunner.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 - 遠(yuǎn)程回調(diào)開始:1234 接收到信息: 1234-result 2018-08-10 18:27:07.964 INFO [Thread-2] com.github.houbb.spring.lean.core.ioc.event.BookingService:73 - 監(jiān)聽到遠(yuǎn)程的信息: 1234-result 2018-08-10 18:27:07.964 INFO [Thread-2] com.github.houbb.spring.lean.core.ioc.event.BookingService:75 - 監(jiān)聽到遠(yuǎn)程消息后: 1234-result 2018-08-10 18:27:07.964 INFO [Thread-2] com.github.houbb.spring.lean.core.ioc.event.BookingService:106 - 已經(jīng)觸發(fā)event 2018-08-10 18:27:07.964 INFO [main] com.github.houbb.spring.lean.core.ioc.event.BookingService:67 - 查詢結(jié)果: 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超時(shí)和空循環(huán) 空循環(huán)
空循環(huán)會(huì)導(dǎo)致 cpu 飆升
while(true) { }
解決方式
while(true) { // 小睡即可 TimeUnit.sleep(1); }超時(shí)編寫
不可能一直等待反饋,可以設(shè)置超時(shí)時(shí)間。
/** * 循環(huán)等待直到獲取結(jié)果 * @param key key * @param timeoutInSeconds 超時(shí)時(shí)間 * @param代碼地址泛型 * @return 結(jié)果。如果超時(shí)則拋出異常 */ public T loopWaitForValue(final String key, long timeoutInSeconds) { long startTime = System.nanoTime(); long deadline = startTime + TimeUnit.SECONDS.toNanos(timeoutInSeconds); //1. 如果沒有新回調(diào),或者 key 對(duì)應(yīng)元素不存在。則一直循環(huán) while(ObjectUtil.isNull(map.get(key))) { try { TimeUnit.MILLISECONDS.sleep(5); } catch (InterruptedException e) { LOGGER.warn("Loop meet InterruptedException, just ignore it.", e); } // 超時(shí)判斷 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. 獲取到元素之后,需要移除掉對(duì)應(yīng)的值 map.remove(key); return target; }
loop
countdownlatch
spring-event-listener
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/76695.html
摘要:創(chuàng)建線程的方式方式一將類聲明為的子類。將該線程標(biāo)記為守護(hù)線程或用戶線程。其中方法隱含的線程為父線程。恢復(fù)線程,已過時(shí)。等待該線程銷毀終止。更多的使當(dāng)前線程在鎖存器倒計(jì)數(shù)至零之前一直等待,除非線 知識(shí)體系圖: showImg(https://segmentfault.com/img/bVbef6v?w=1280&h=960); 1、線程是什么? 線程是進(jìn)程中獨(dú)立運(yùn)行的子任務(wù)。 2、創(chuàng)建線...
摘要:在多線程編程中我們會(huì)遇到很多需要使用線程同步機(jī)制去解決的并發(fā)問題,而這些同步機(jī)制就是多線程編程中影響正確性和運(yùn)行效率的重中之重。這五個(gè)方法之所以能指定同步器的行為,則是因?yàn)橹械钠渌椒ň褪峭ㄟ^對(duì)這五個(gè)方法的調(diào)用來實(shí)現(xiàn)的。 在多線程編程中我們會(huì)遇到很多需要使用線程同步機(jī)制去解決的并發(fā)問題,而這些同步機(jī)制就是多線程編程中影響正確性和運(yùn)行效率的重中之重。這不禁讓我感到好奇,這些同步機(jī)制是如何...
摘要:典型地,和被用在等待另一個(gè)線程產(chǎn)生的結(jié)果的情形測(cè)試發(fā)現(xiàn)結(jié)果還沒有產(chǎn)生后,讓線程阻塞,另一個(gè)線程產(chǎn)生了結(jié)果后,調(diào)用使其恢復(fù)。使當(dāng)前線程放棄當(dāng)前已經(jīng)分得的時(shí)間,但不使當(dāng)前線程阻塞,即線程仍處于可執(zhí)行狀態(tài),隨時(shí)可能再次分得時(shí)間。 1、說說進(jìn)程,線程,協(xié)程之間的區(qū)別 簡(jiǎn)而言之,進(jìn)程是程序運(yùn)行和資源分配的基本單位,一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線程.進(jìn)程在執(zhí)行過程中擁有獨(dú)立的內(nèi)存單元...
摘要:在創(chuàng)建對(duì)象時(shí),需要轉(zhuǎn)入一個(gè)值,用于初始化的成員變量,該成員變量表示屏障攔截的線程數(shù)。當(dāng)?shù)竭_(dá)屏障的線程數(shù)小于時(shí),這些線程都會(huì)被阻塞住。當(dāng)所有線程到達(dá)屏障后,將會(huì)被更新,表示進(jìn)入新一輪的運(yùn)行輪次中。 1.簡(jiǎn)介 在分析完AbstractQueuedSynchronizer(以下簡(jiǎn)稱 AQS)和ReentrantLock的原理后,本文將分析 java.util.concurrent 包下的兩個(gè)...
摘要:但是單核我們還是要應(yīng)用多線程,就是為了防止阻塞。多線程可以防止這個(gè)問題,多條線程同時(shí)運(yùn)行,哪怕一條線程的代碼執(zhí)行讀取數(shù)據(jù)阻塞,也不會(huì)影響其它任務(wù)的執(zhí)行。 1、多線程有什么用?一個(gè)可能在很多人看來很扯淡的一個(gè)問題:我會(huì)用多線程就好了,還管它有什么用?在我看來,這個(gè)回答更扯淡。所謂知其然知其所以然,會(huì)用只是知其然,為什么用才是知其所以然,只有達(dá)到知其然知其所以然的程度才可以說是把一個(gè)知識(shí)點(diǎn)...
閱讀 1873·2023-04-26 02:46
閱讀 2003·2021-11-25 09:43
閱讀 1147·2021-09-29 09:35
閱讀 2104·2019-08-30 15:56
閱讀 3426·2019-08-30 15:54
閱讀 2637·2019-08-29 16:35
閱讀 3124·2019-08-29 15:25
閱讀 3294·2019-08-29 14:01