Java中將異步調(diào)用轉(zhuǎn)為同步的五種實現(xiàn)方法
異步與同步的核心區(qū)別
- 同步調(diào)用:調(diào)用方阻塞等待結(jié)果返回
- 異步調(diào)用:調(diào)用方立即返回,通過回調(diào)/輪詢等方式獲取結(jié)果
本文重點討論如何將異步調(diào)用轉(zhuǎn)為同步阻塞模式,以下是五種實現(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);
}
}
// 消費者線程
public void consume() throws InterruptedException {
while (true) {
synchronized (lock) {
while (count == 0) {
System.out.println("緩沖區(qū)為空,消費者等待...");
lock.wait();
}
int value = buffer[--count];
System.out.println("消費數(shù)據(jù): " + value + ",緩沖區(qū)數(shù)量: " + count);
lock.notify();
}
Thread.sleep(1500);
}
}
public static void main(String[] args) {
ProducerConsumerExample example = new ProducerConsumerExample();
// 啟動生產(chǎn)者和消費者線程
new Thread(example::produce).start();
new Thread(example::consume).start();
}
}
關鍵要點
共享資源保護:通過synchronized(lock)?保證線程安全
條件判斷:
- ?while?循環(huán)而非if?防止虛假喚醒
- 緩沖區(qū)滿時生產(chǎn)者等待(wait()?)
- 緩沖區(qū)空時消費者等待(wait()?)
協(xié)作機制:每次操作后通過notify()?喚醒等待線程
方法對比:
- ?notify()?:喚醒單個等待線程
- ?notifyAll()?:喚醒所有等待線程(適用于多生產(chǎn)者場景)
方法二:使用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();
}
}
}
核心特性
多條件支持:
- 一個鎖對象可綁定多個Condition(如moneyCondition/ticketCondition)
精準喚醒:
- ?await()?:釋放鎖并等待特定條件
- ?signal()?:喚醒滿足條件的等待線程
代碼結(jié)構:
- 必須在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í)行其他任務...");
try {
Integer result = future.get(2, TimeUnit.SECONDS);
System.out.println("計算結(jié)果: 1+2+...+100 = " + result);
} catch (TimeoutException e) {
System.err.println("計算超時!");
future.cancel(true);
} catch (Exception e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
}
}
關鍵API
| 方法 | 作用 |
|---|---|
| ?future.get()? | 阻塞獲取結(jié)果(可設置超時) |
| ?future.cancel()? | 取消任務執(zhí)行 |
| ?isDone()? | 檢查任務是否完成 |
執(zhí)行流程
- 提交Callable?任務到線程池
- 主線程繼續(xù)執(zhí)行其他操作
- 調(diào)用future.get()?阻塞等待結(jié)果
- 處理可能出現(xià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("運動員" + i + "正在準備...");
TimeUnit.MILLISECONDS.sleep(300);
readySignal.countDown();
startSignal.await();
System.out.println("運動員" + i + "起跑!");
TimeUnit.MILLISECONDS.sleep((long)(Math.random() * 1000));
System.out.println("運動員" + i + "到達終點!");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
System.out.println("裁判等待運動員就位...");
readySignal.await();
System.out.println("\n所有運動員就位!");
TimeUnit.SECONDS.sleep(1);
System.out.println("發(fā)令槍響!");
startSignal.countDown();
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
System.out.println("\n比賽結(jié)束!");
}
}
應用場景
- 多線程初始化后統(tǒng)一執(zhí)行:如服務啟動時等待所有組件就緒
- 并發(fā)測試控制:模擬固定數(shù)量請求同時發(fā)起
- 事件驅(qū)動編程:等待多個前置條件完成
方法五: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===== 進入下一階段 ====="));
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("測試部署", 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);
}
}
}
核心特性
| 對比項 | CountDownLatch | CyclicBarrier |
|---|---|---|
| 重用性 | 一次性使用 | 可重復觸發(fā) |
| 線程關系 | 主線程等待子線程 | 子線程相互等待 |
| 典型場景 | 線程初始化完成后執(zhí)行 | 多階段任務協(xié)作 |
總結(jié)對比表
| 方法 | 適用場景 | 核心機制 | 擴展性 |
|---|---|---|---|
| wait/notify | 簡單生產(chǎn)者-消費者模型 | 對象鎖的等待/通知機制 | 低 |
| ReentrantLock+Condition | 需要多個條件變量 | 精細條件控制 | 中 |
| Future | 異步任務結(jié)果獲取 | 任務提交與結(jié)果回調(diào) | 高 |
| CountDownLatch | 多線程等待單一事件 | 計數(shù)器遞減觸發(fā)機制 | 中 |
| CyclicBarrier | 多階段任務同步 | 可重置的屏障計數(shù)機制 | 高 |
最佳實踐建議:
- 簡單同步場景優(yōu)先使用CountDownLatch?
- 需要結(jié)果返回時使用Future?
- 多條件或多階段場景推薦CyclicBarrier?
- 避免使用過時的Object.wait/notify?直接控制
以上就是Java中將異步調(diào)用轉(zhuǎn)為同步的五種方法的詳細內(nèi)容,更多關于Java異步調(diào)用轉(zhuǎn)同步的資料請關注腳本之家其它相關文章!
相關文章
JAVA中StringBuffer與String的區(qū)別解析
這篇文章主要介紹了JAVA中StringBuffer與String的區(qū)別解析,需要的朋友可以參考下2014-02-02
springboot 動態(tài)數(shù)據(jù)源的實現(xiàn)方法(Mybatis+Druid)
這篇文章主要介紹了springboot 動態(tài)數(shù)據(jù)源的實現(xiàn)方法(Mybatis+Druid),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01
Java C++題解leetcode 1684統(tǒng)計一致字符串的數(shù)目示例
這篇文章主要為大家介紹了Java C++題解leetcode 1684統(tǒng)計一致字符串的數(shù)目示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-01-01
springboot實現(xiàn)配置兩個parent的方法
這篇文章主要介紹了springboot實現(xiàn)配置兩個parent的方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
淺談JDK8中的Duration Period和ChronoUnit
在JDK8中,引入了三個非常有用的時間相關的API:Duration,Period和ChronoUnit。他們都是用來對時間進行統(tǒng)計的,本文將會詳細講解一下這三個API的使用2021-06-06

