Java中將異步調(diào)用轉(zhuǎn)為同步的五種實(shí)現(xiàn)方法
異步與同步的核心區(qū)別
- 同步調(diào)用:調(diào)用方阻塞等待結(jié)果返回
- 異步調(diào)用:調(diào)用方立即返回,通過回調(diào)/輪詢等方式獲取結(jié)果
本文重點(diǎn)討論如何將異步調(diào)用轉(zhuǎn)為同步阻塞模式,以下是五種實(shí)現(xiàn)方案:
方法一:使用wait/notify + synchronized
代碼示例
public class ProducerConsumerExample { private static final int BUFFER_SIZE = 5; private final Object lock = new Object(); private int[] buffer = new int[BUFFER_SIZE]; private int count = 0; // 生產(chǎn)者線程 public void produce() throws InterruptedException { int value = 0; while (true) { synchronized (lock) { while (count == BUFFER_SIZE) { System.out.println("緩沖區(qū)已滿,生產(chǎn)者等待..."); lock.wait(); } buffer[count++] = value++; System.out.println("生產(chǎn)數(shù)據(jù): " + value + ",緩沖區(qū)數(shù)量: " + count); lock.notify(); } Thread.sleep(1000); } } // 消費(fèi)者線程 public void consume() throws InterruptedException { while (true) { synchronized (lock) { while (count == 0) { System.out.println("緩沖區(qū)為空,消費(fèi)者等待..."); lock.wait(); } int value = buffer[--count]; System.out.println("消費(fèi)數(shù)據(jù): " + value + ",緩沖區(qū)數(shù)量: " + count); lock.notify(); } Thread.sleep(1500); } } public static void main(String[] args) { ProducerConsumerExample example = new ProducerConsumerExample(); // 啟動(dòng)生產(chǎn)者和消費(fèi)者線程 new Thread(example::produce).start(); new Thread(example::consume).start(); } }
關(guān)鍵要點(diǎn)
共享資源保護(hù):通過synchronized(lock)?保證線程安全
條件判斷:
- ?while?循環(huán)而非if?防止虛假喚醒
- 緩沖區(qū)滿時(shí)生產(chǎn)者等待(wait()?)
- 緩沖區(qū)空時(shí)消費(fèi)者等待(wait()?)
協(xié)作機(jī)制:每次操作后通過notify()?喚醒等待線程
方法對(duì)比:
- ?notify()?:?jiǎn)拘褑蝹€(gè)等待線程
- ?notifyAll()?:?jiǎn)拘阉械却€程(適用于多生產(chǎn)者場(chǎng)景)
方法二:使用ReentrantLock + Condition
代碼示例
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class TestReentrantLock4 { static ReentrantLock lock = new ReentrantLock(); static Condition moneyCondition = lock.newCondition(); static Condition ticketCondition = lock.newCondition(); static boolean haveMoney = false; static boolean haveTicket = false; public static void main(String[] args) throws InterruptedException { // 農(nóng)民1(等錢) new Thread(() -> { lock.lock(); try { while (!haveMoney) { System.out.println("農(nóng)民1等待資金..."); moneyCondition.await(); } System.out.println("農(nóng)民1獲得資金,回家!"); } finally { lock.unlock(); } }, "Farmer1").start(); // 農(nóng)民2(等票) new Thread(() -> { lock.lock(); try { while (!haveTicket) { System.out.println("農(nóng)民2等待車票..."); ticketCondition.await(); } System.out.println("農(nóng)民2獲得車票,回家!"); } finally { lock.unlock(); } }, "Farmer2").start(); // 主線程模擬發(fā)放條件 Thread.sleep(1000); lock.lock(); try { haveMoney = true; moneyCondition.signal(); System.out.println("資金已發(fā)放!"); haveTicket = true; ticketCondition.signal(); System.out.println("車票已發(fā)放!"); } finally { lock.unlock(); } } }
核心特性
多條件支持:
- 一個(gè)鎖對(duì)象可綁定多個(gè)Condition(如moneyCondition/ticketCondition)
精準(zhǔn)喚醒:
- ?await()?:釋放鎖并等待特定條件
- ?signal()?:?jiǎn)拘褲M足條件的等待線程
代碼結(jié)構(gòu):
- 必須在lock.lock()?和finally unlock()?之間操作
- 條件判斷使用while?循環(huán)防止虛假喚醒
方法三:Future(Callable + ExecutorService)
代碼示例
import java.util.concurrent.*; public class FutureExample { public static void main(String[] args) { ExecutorService executor = Executors.newSingleThreadExecutor(); Future<Integer> future = executor.submit(() -> { int sum = 0; for (int i = 1; i <= 100; i++) { sum += i; Thread.sleep(10); } return sum; }); System.out.println("主線程執(zhí)行其他任務(wù)..."); try { Integer result = future.get(2, TimeUnit.SECONDS); System.out.println("計(jì)算結(jié)果: 1+2+...+100 = " + result); } catch (TimeoutException e) { System.err.println("計(jì)算超時(shí)!"); future.cancel(true); } catch (Exception e) { e.printStackTrace(); } finally { executor.shutdown(); } } }
關(guān)鍵API
方法 | 作用 |
---|---|
?future.get()? | 阻塞獲取結(jié)果(可設(shè)置超時(shí)) |
?future.cancel()? | 取消任務(wù)執(zhí)行 |
?isDone()? | 檢查任務(wù)是否完成 |
執(zhí)行流程
- 提交Callable?任務(wù)到線程池
- 主線程繼續(xù)執(zhí)行其他操作
- 調(diào)用future.get()?阻塞等待結(jié)果
- 處理可能出現(xiàn)的異常情況
- 最終關(guān)閉線程池資源
方法四:CountDownLatch(多線程同步)
代碼示例
import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; public class CountDownLatchExample { private static final int RUNNERS = 5; private static final CountDownLatch startSignal = new CountDownLatch(1); private static final CountDownLatch readySignal = new CountDownLatch(RUNNERS); public static void main(String[] args) throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(RUNNERS); for (int i = 1; i <= RUNNERS; i++) { executor.execute(() -> { try { System.out.println("運(yùn)動(dòng)員" + i + "正在準(zhǔn)備..."); TimeUnit.MILLISECONDS.sleep(300); readySignal.countDown(); startSignal.await(); System.out.println("運(yùn)動(dòng)員" + i + "起跑!"); TimeUnit.MILLISECONDS.sleep((long)(Math.random() * 1000)); System.out.println("運(yùn)動(dòng)員" + i + "到達(dá)終點(diǎn)!"); } catch (InterruptedException e) { e.printStackTrace(); } }); } System.out.println("裁判等待運(yùn)動(dòng)員就位..."); readySignal.await(); System.out.println("\n所有運(yùn)動(dòng)員就位!"); TimeUnit.SECONDS.sleep(1); System.out.println("發(fā)令槍響!"); startSignal.countDown(); executor.shutdown(); executor.awaitTermination(5, TimeUnit.SECONDS); System.out.println("\n比賽結(jié)束!"); } }
應(yīng)用場(chǎng)景
- 多線程初始化后統(tǒng)一執(zhí)行:如服務(wù)啟動(dòng)時(shí)等待所有組件就緒
- 并發(fā)測(cè)試控制:模擬固定數(shù)量請(qǐng)求同時(shí)發(fā)起
- 事件驅(qū)動(dòng)編程:等待多個(gè)前置條件完成
方法五:CyclicBarrier(可重用同步屏障)
代碼示例
import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; public class CyclicBarrierExample { private static final CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("\n===== 進(jìn)入下一階段 =====")); public static void main(String[] args) { for (int i = 1; i <= 3; i++) { new Thread(new TeamMember(i)).start(); } } static class TeamMember implements Runnable { private int id; public TeamMember(int id) { this.id = id; } @Override public void run() { try { doWork("需求分析", 1000); barrier.await(); doWork("開發(fā)編碼", 1500); barrier.await(); doWork("測(cè)試部署", 800); barrier.await(); } catch (Exception e) { e.printStackTrace(); } } private void doWork(String phase, int baseTime) throws InterruptedException { int time = baseTime + (int)(Math.random() * 500); System.out.printf("%s 完成%s(%dms)\n", Thread.currentThread().getName(), phase, time); Thread.sleep(time); } } }
核心特性
對(duì)比項(xiàng) | CountDownLatch | CyclicBarrier |
---|---|---|
重用性 | 一次性使用 | 可重復(fù)觸發(fā) |
線程關(guān)系 | 主線程等待子線程 | 子線程相互等待 |
典型場(chǎng)景 | 線程初始化完成后執(zhí)行 | 多階段任務(wù)協(xié)作 |
總結(jié)對(duì)比表
方法 | 適用場(chǎng)景 | 核心機(jī)制 | 擴(kuò)展性 |
---|---|---|---|
wait/notify | 簡(jiǎn)單生產(chǎn)者-消費(fèi)者模型 | 對(duì)象鎖的等待/通知機(jī)制 | 低 |
ReentrantLock+Condition | 需要多個(gè)條件變量 | 精細(xì)條件控制 | 中 |
Future | 異步任務(wù)結(jié)果獲取 | 任務(wù)提交與結(jié)果回調(diào) | 高 |
CountDownLatch | 多線程等待單一事件 | 計(jì)數(shù)器遞減觸發(fā)機(jī)制 | 中 |
CyclicBarrier | 多階段任務(wù)同步 | 可重置的屏障計(jì)數(shù)機(jī)制 | 高 |
最佳實(shí)踐建議:
- 簡(jiǎn)單同步場(chǎng)景優(yōu)先使用CountDownLatch?
- 需要結(jié)果返回時(shí)使用Future?
- 多條件或多階段場(chǎng)景推薦CyclicBarrier?
- 避免使用過時(shí)的Object.wait/notify?直接控制
以上就是Java中將異步調(diào)用轉(zhuǎn)為同步的五種方法的詳細(xì)內(nèi)容,更多關(guān)于Java異步調(diào)用轉(zhuǎn)同步的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring Boot開發(fā)編譯后讀取不到@spring.profiles.active@的問題及解決步驟
這篇文章主要介紹了Spring Boot開發(fā)編譯后讀取不到@spring.profiles.active@的問題及解決步驟,需要的朋友可以參考下2024-12-12Spring?MVC DispatcherServlet處理請(qǐng)求過程示例詳解
這篇文章主要介紹了Spring?MVC?DispatcherServlet處理請(qǐng)求過程示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09SpringBoot實(shí)現(xiàn)自定義條件注解的代碼示例
在Spring Boot中,條件注解是一種非常強(qiáng)大的工具,它可以根據(jù)特定的條件來選擇是否加載某個(gè)類或某個(gè)Bean,文將介紹如何在Spring Boot中實(shí)現(xiàn)自定義條件注解,并提供一個(gè)示例代碼,需要的朋友可以參考下2023-06-06解決springboot 無法配置多個(gè)靜態(tài)路徑的問題
這篇文章主要介紹了解決springboot 無法配置多個(gè)靜態(tài)路徑的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08java使用內(nèi)存數(shù)據(jù)庫(kù)ssdb的步驟
這篇文章主要介紹了java使用內(nèi)存數(shù)據(jù)庫(kù)ssdb的步驟,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-12-12