詳解Java中異步轉(zhuǎn)同步的六種方法
一、問題
應(yīng)用場景
應(yīng)用中通過框架發(fā)送異步命令時,不能立刻返回命令的執(zhí)行結(jié)果,而是異步返回命令的執(zhí)行結(jié)果。
那么,問題來了,針對應(yīng)用中這種異步調(diào)用,能不能像同步調(diào)用一樣立刻獲取到命令的執(zhí)行結(jié)果,如何實現(xiàn)異步轉(zhuǎn)同步?
二、分析
首先,解釋下同步和異步
- 同步,就是發(fā)出一個調(diào)用時,在沒有得到結(jié)果之前,該調(diào)用就不返回或繼續(xù)執(zhí)行后續(xù)操作。
- 異步,當(dāng)一個異步過程調(diào)用發(fā)出后,調(diào)用者在沒有得到結(jié)果之前,就可以繼續(xù)執(zhí)行后續(xù)操作。當(dāng)這個調(diào)用完成后,一般通過狀態(tài)、通知和回調(diào)來通知調(diào)用者。
對于異步調(diào)用,調(diào)用的返回并不受調(diào)用者控制。
異步轉(zhuǎn)同步主要實現(xiàn)思路:所有實現(xiàn)原理類似,是在發(fā)出調(diào)用的線程中進行阻塞等待結(jié)果,調(diào)用完成后通過回調(diào)、設(shè)置共享狀態(tài)或通知進行阻塞狀態(tài)的解除,繼續(xù)執(zhí)行后續(xù)操作。
三、實現(xiàn)方法
通常,實現(xiàn)中,不會無限的等待,一般會設(shè)定一個超時時間,具體超時時間根據(jù)具體場景確定。
下面以回調(diào)的方式介紹幾種常用實現(xiàn)異步轉(zhuǎn)同步的方法:
1.輪詢與休眠重試機制
采用輪詢與休眠重試機制,線程將反復(fù)在休眠和測試狀態(tài)條件中之間切換,直到超時或者狀態(tài)條件滿足繼續(xù)向下執(zhí)行。這種方式,超時時間控制不準(zhǔn)確,sleep時間需要在響應(yīng)性和CPU使用率之間進行權(quán)衡。
private static long MILLIS_OF_WAIT_TIME = 300000L;// 等待時間 5分鐘 private final Object lock = new Object(); //3.結(jié)果返回后進行回調(diào),解除阻塞 @Override public void callback(AsynResponse response){ synchronized(lock){ //設(shè)置狀態(tài)條件 } public Result getResult() throws ErrorCodeException { // 1.異步調(diào)用 // 2.阻塞等待異步響應(yīng) long future = System.currentTimeMillis() + MILLIS_OF_WAIT_TIME; long remaining = MILLIS_OF_WAIT_TIME;//剩余等待時間 while(remaining > 0){ synchronized(lock){ if(狀態(tài)條件未滿足){ remaining = future - System.currentTimeMillis(); Thread.sleep(時間具體場景確定); } } ````} //4.超時或結(jié)果正確返回,對結(jié)果進行處理 return result; }
2.wait/notify
任意一個Java對象,都擁有一組監(jiān)視器方法(wait、notify、notifyAll等方法),這些方法和synchronized同步關(guān)鍵字配合,可以實現(xiàn)等待/通知模式。但是使用wait/notify,使線程的阻塞/喚醒對線程本身來說是被動的,要準(zhǔn)確的控制哪個線程是很困難的,所以是要么隨機喚醒等待在條件隊列上一個線程(notify),要么喚醒所有的(notifyAll,但是很低效)。當(dāng)多個線程基于不同條件在同一條件隊列上等待時,如果使用notify而不是notifyAll,很容易導(dǎo)致信號丟失的問題,所以必須謹(jǐn)慎使用wait/notify方法。
private static long MILLIS_OF_WAIT_TIME = 300000L;// 等待時間 5分鐘 private final Object lock = new Object(); //3.結(jié)果返回后進行回調(diào),解除阻塞 @Override public void callback(AsynResponse response){ synchronized(lock){ lock.notifyAll(); } public Result getResult() throws ErrorCodeException { // 1.異步調(diào)用 // 2.阻塞等待異步響應(yīng) long future = System.currentTimeMillis() + MILLIS_OF_WAIT_TIME; long remaining = MILLIS_OF_WAIT_TIME;//剩余等待時間 synchronized(lock){ while(條件未滿足 && remaining > 0){ //被通知后要檢查條件 lock.wait(remaining); remaining = future - System.currentTimeMillis(); } ````} //4.超時或結(jié)果正確返回,對結(jié)果進行處理 return result; }
3.Lock Condition
使用Lock的Condition隊列的實現(xiàn)方式和wait/notify方式類似,但是Lock支持多個Condition隊列,并且支持等待狀態(tài)中響應(yīng)中斷。
private static long SECONDS_OF_WAIT_TIME = 300L;// 等待時間 5分鐘 private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); //3.結(jié)果返回后進行回調(diào),解除阻塞 @Override public void callback(AsynResponse response){ lock.lock();//這是前提 try { condition.signal(); }finally { lock.unlock(); } } public Result getResult() throws ErrorCodeException { // 1.異步調(diào)用 // 2.阻塞等待異步響應(yīng) lock.lock();//這是前提 try { condition.await(); } catch (InterruptedException e) { //TODO }finally { lock.unlock(); } //4.超時或結(jié)果正確返回,對結(jié)果進行處理 return result; }
4.CountDownLatch
使用CountDownLatch可以實現(xiàn)異步轉(zhuǎn)同步,它好比計數(shù)器,在創(chuàng)建實例CountDownLatch對象的時候傳入數(shù)字,每使用一次 countDown() 方法計數(shù)減1,當(dāng)數(shù)字減到0時, await()方法后的代碼將可以執(zhí)行,未到0之前將一直阻塞等待。
private static long SECONDS_OF_WAIT_TIME = 300L;// 等待時間 5分鐘 private final CountDownLatch countDownLatch = new CountDownLatch(1); //3.結(jié)果返回后進行回調(diào),解除阻塞 @Override public void callback(AsynResponse response){ countDownLatch.countDown(); } public Result getResult() throws ErrorCodeException { // 1.異步調(diào)用 // 2.阻塞等待異步響應(yīng) try { countDownLatch.await(SECONDS_OF_WAIT_TIME, TimeUnit.SECONDS); } catch (InterruptedException e) { //TODO } //4.超時或結(jié)果正確返回,對結(jié)果進行處理 return result; }
5.CyclicBarrier
讓一組線程達到一個屏障(也可以叫同步點)時被阻塞,直到等待最后一個線程到達屏障時,屏障才開門,所有被屏障攔截的線程才會繼續(xù)執(zhí)行。
每個線程通過調(diào)用await方法告訴CyclicBarrier我已經(jīng)到達了屏障,然后當(dāng)前的的線程被阻塞。
private static long SECONDS_OF_WAIT_TIME = 300L;// 等待時間 5分鐘 private final CountDownLatch cyclicBarrier= new CyclicBarrier(2);//設(shè)置屏障攔截的線程數(shù)為2 //3.結(jié)果返回后進行回調(diào),解除阻塞 @Override public void callback(AsynResponse response){ //我也到達屏障了,可以開門了 cyclicBarrier.await(); } public Result getResult() throws ErrorCodeException { // 1.異步調(diào)用 // 2.阻塞等待異步響應(yīng) try { //我到達屏障了,還沒開門,要等一等 cyclicBarrier.await(SECONDS_OF_WAIT_TIME, TimeUnit.SECONDS); } catch (InterruptedException e) { //TODO } //4.超時或結(jié)果正確返回,對結(jié)果進行處理 return result; }
CountDownLatch和CyclicBarrier實現(xiàn)類似,區(qū)別是CountDownLatch的計數(shù)器只能使用一次,而CyclicBarrier的計數(shù)器可以使用reset重置,
所以CyclicBarrier能處理更為復(fù)雜的業(yè)務(wù)場景。在異步轉(zhuǎn)同步中,計數(shù)器不會重用,所以使用CountDownLatch實現(xiàn)更適合。
6.LockSupport
LockSupport定義了一組公共靜態(tài)方法,提供了最基本的線程阻塞和喚醒的方法。
private static long NANOS_OF_WAIT_TIME = 300000000L;// 等待時間 5分鐘 private final LockSupport lockSupport = new LockSupport(); //3.結(jié)果返回后進行回調(diào),解除阻塞 @Override public void callback(AsynResponse response){ lockSupport.unpark(); } public Result getResult() throws ErrorCodeException { // 1.異步調(diào)用 // 2.阻塞等待異步響應(yīng) try { lockSupport.parkNanos(NANOS_OF_WAIT_TIME); } catch (InterruptedException e) { //TODO } //4.超時或結(jié)果正確返回,對結(jié)果進行處理 return result; }
到此這篇關(guān)于詳解Java中異步轉(zhuǎn)同步的六種方法的文章就介紹到這了,更多相關(guān)Java異步轉(zhuǎn)同步內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot 注冊服務(wù)注冊中心(zk)的兩種方式詳解
本文通過一個demo講述一下這兩種注冊方式,使用的是傳統(tǒng)的向zk注冊的方案。對springboot 注冊zk的相關(guān)知識感興趣的朋友一起看看吧2018-01-01如何解決 Java 中的 IndexOutOfBoundsException 異
當(dāng)我們在 Java 中使用 List 的時候,有時候會出現(xiàn)向 List 中不存在的位置設(shè)置新元素的情況,從而導(dǎo)致 IndexOutOfBoundsException 異常,本文將會介紹這個問題的產(chǎn)生原因以及解決方案2023-10-10java中 IO 常用IO操作類繼承結(jié)構(gòu)分析
本篇文章小編為大家介紹,java中 IO 常用IO操作類繼承結(jié)構(gòu)分析。需要的朋友參考下2013-04-04