javascript
java异步处理同步化_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());
@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(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 - 遠程回調開始: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異步調用轉同步的5種方式
1.異步和同步的概念 同步調用:調用方在調用過程中,持續等待返回結果. 異步調用:調用方在調用過程中,不直接等待返回結果,而是執行其他任務,結果返回形式通常為回調函數. 2 .異步轉為同步的概率 需要 ...
java 異步機制與同步機制的區別
所謂異步輸入輸出機制,是指在進行輸入輸出處理時,不必等到輸入輸出處理完畢才返回.所以異步的同義語是非阻塞(None Blocking).?網上有很多網友用很通俗的比喻 ?把同步和異步講解的很透徹 轉過 ...
異步查詢轉同步加redis業務實現的BUG分享
在最近的性能測試中,某一個查詢接口指標不通過,開發做了N次優化,最終的優化方案如下:異步查詢然后轉同步,再加上redis緩存.此為背景. 在測試過程中發現一個BUG:同樣的請求在第一次查詢結果是OK的 ...
5種必會的Java異步調用轉同步的方法你會幾種
轉載請注明本文地址:https://www.jianshu.com/p/f00aa6f66281 源碼地址:https://gitee.com/sunnymore/asyncToSync Sunny先 ...
java中全面的單例模式多種實現方式總結
單例模式的思想 想整理一些 java 并發相關的知識,不知道從哪開始,想起了單例模式中要考慮的線程安全,就從單例模式開始吧. 以前寫過單例模式,這里再重新匯總補充整理一下,單例模式的多種實現. 單例模 ...
java 多線程并發 synchronized 同步機制及方式
2. 鎖機制 3. 并發 Excutor框架 4.?并發性與多線程介紹 1. synchronized? 參考1. synchronized 分兩種方式進行線程的同步:同步塊.同步方法 1. 方法同步 ...
java異步編程降低延遲
目錄 java異步編程降低延遲 一.ExecutorService和CompletionService 二.CompletableFuture(重要) 三.stream中的parallel(并行流) ...
java筆記--關于線程同步(7種同步方式)
關于線程同步(7種方式) --如果朋友您想轉載本文章請注明轉載地址"http://www.cnblogs.com/XHJT/p/3897440.html"謝謝-- 為何要使用同步? ...
java 獲取classpath下文件多種方式
java 獲取classpath下文件多種方式 一:properties下配置 在resources下定義server.properties register.jks.path=classpath\: ...
隨機推薦
CSS3動畫事件
CSS3 的動畫效果強大,在移動端使用廣泛,動畫執行開始和結束都可以使用JS來監聽其事件. animationstart animationend 以下是一個示例 ...
oracle ebs應用產品安全性-交叉驗證規則
轉自: http://blog.itpub.net/298600/viewspace-625138/ 定義: Oracle鍵彈性域可以根據自定義鍵彈性域時所定義的規則,執行段值組合的自動交叉驗證.使用 ...
Auto Updating the exe from a network location when application starts z
http://www.codeproject.com/Tips/869588/Auto-Updating-the-exe-from-a-network-location-when?msg=499218 ...
Android+Jquery Mobile學習系列(4)-頁面跳轉及參數傳遞
關于頁面轉場,這個必須得專門列出來說明一下,因為Jquery Mobile與普通的Web發開有一些區別,這個對于新手如果不了解的話,就會鉆到死胡同.擼主前段時間就是很急躁地上手開發程序,結果在頁面轉場 ...
sql查詢統計,根據新聞類別ID統計,沒有數據顯示0
有兩張表,新聞信息表MessageInfo和新聞類別表MessageType.表結構如下: 然后需要實現下面這種查詢結果: 這個是我面試時遇到的,上面的新聞類型是亂寫的.當時沒有做出來,然后回來又研究 ...
[Linux]信號集和sigprocmask信號屏蔽函數
一.概述 系統提供這樣一種能力,就是創建一個信號集,然后傳遞給信號屏蔽函數,從而屏蔽向該進程發送的信號. 有一點需要注意的是,不能屏蔽SIGKILL和SIGSTOP信號. 信號集是sigset_t類型 ...
gitlab 502
經過一個下午的查找終于發現了錯誤,原來是在服務器上還開啟了一個tomcat服務,占用了8080端口,使GitLab的unicorn服務不能開啟. 最后在/etc/gitlab/gitlab.rb 中做 ...
P4381 [IOI2008]Island(基環樹+單調隊列優化dp)
P4381 [IOI2008]Island 題意:求圖中所有基環樹的直徑和 我們對每棵基環樹分別計算答案. 首先我們先bfs找環(dfs易爆棧) 藍后我們處理直徑 直徑不在環上,就在環上某點的子樹上 ...
Laravel JsonResponse數組獲取
有一個JsonResponse數據的格式如下: object(Illuminate\Http\JsonResponse)[474] protected 'data' => string '{&q ...
js如何切割字符串
總結
以上是生活随笔為你收集整理的java异步处理同步化_java 异步查询转同步多种实现方式:循环等待,CountDownLatch,Spring EventListener,超时处理和空循环性能优化...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python计算球坐标系的积分_pyth
- 下一篇: python中常见的15中面试题