Java中将异步调用转为同步的五种方法

异步与同步的核心区别

  • 同步调用:调用方阻塞等待结果返回
  • 异步调用:调用方立即返回,通过回调/轮询等方式获取结果

本文重点讨论如何将异步调用转为同步阻塞模式,以下是五种实现方案:

方法一:使用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;
 
     // 生产者线程
     public void produce() throws InterruptedException {
         int value = 0;
         while (true) {
             synchronized (lock) {
                 while (count == BUFFER_SIZE) {
                     System.out.println("缓冲区已满,生产者等待...");
                     lock.wait();
                 }
                 buffer[count++] = value++;
                 System.out.println("生产数据: " + value + ",缓冲区数量: " + count);
                 lock.notify();
             }
             Thread.sleep(1000);
         }
     }
 
     // 消费者线程
     public void consume() throws InterruptedException {
         while (true) {
             synchronized (lock) {
                 while (count == 0) {
                     System.out.println("缓冲区为空,消费者等待...");
                     lock.wait();
                 }
                 int value = buffer[--count];
                 System.out.println("消费数据: " + value + ",缓冲区数量: " + count);
                 lock.notify();
             }
             Thread.sleep(1500);
         }
     }
 
     public static void main(String[] args) {
         ProducerConsumerExample example = new ProducerConsumerExample();
     
         // 启动生产者和消费者线程
         new Thread(example::produce).start();
         new Thread(example::consume).start();
     }
 }

关键要点

  1. 共享资源保护 :通过synchronized(lock)保证线程安全

  2. 条件判断

    • while循环而非if防止虚假唤醒
    • 缓冲区满时生产者等待(wait())
    • 缓冲区空时消费者等待(wait())
  3. 协作机制 :每次操作后通过notify()唤醒等待线程

  4. 方法对比

    • notify():唤醒单个等待线程
    • notifyAll():唤醒所有等待线程(适用于多生产者场景)

方法二:使用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 {
         // 农民1(等钱)
         new Thread(() -> {
             lock.lock();
             try {
                 while (!haveMoney) {
                     System.out.println("农民1等待资金...");
                     moneyCondition.await();
                 }
                 System.out.println("农民1获得资金,回家!");
             } finally {
                 lock.unlock();
             }
         }, "Farmer1").start();
 
         // 农民2(等票)
         new Thread(() -> {
             lock.lock();
             try {
                 while (!haveTicket) {
                     System.out.println("农民2等待车票...");
                     ticketCondition.await();
                 }
                 System.out.println("农民2获得车票,回家!");
             } finally {
                 lock.unlock();
             }
         }, "Farmer2").start();
 
         // 主线程模拟发放条件
         Thread.sleep(1000);
         lock.lock();
         try {
             haveMoney = true;
             moneyCondition.signal();
             System.out.println("资金已发放!");
 
             haveTicket = true;
             ticketCondition.signal();
             System.out.println("车票已发放!");
         } finally {
             lock.unlock();
         }
     }
 }

核心特性

  1. 多条件支持

    • 一个锁对象可绑定多个Condition(如moneyCondition/ticketCondition)
  2. 精准唤醒

    • await():释放锁并等待特定条件
    • signal():唤醒满足条件的等待线程
  3. 代码结构

    • 必须在lock.lock()finally unlock()之间操作
    • 条件判断使用while循环防止虚假唤醒

方法三: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("主线程执行其他任务...");
     
         try {
             Integer result = future.get(2, TimeUnit.SECONDS);
             System.out.println("计算结果: 1+2+...+100 = " + result);
         } catch (TimeoutException e) {
             System.err.println("计算超时!");
             future.cancel(true);
         } catch (Exception e) {
             e.printStackTrace();
         } finally {
             executor.shutdown();
         }
     }
 }

关键API

方法 作用
future.get() 阻塞获取结果(可设置超时)
future.cancel() 取消任务执行
isDone() 检查任务是否完成

执行流程

  1. 提交Callable任务到线程池
  2. 主线程继续执行其他操作
  3. 调用future.get()阻塞等待结果
  4. 处理可能出现的异常情况
  5. 最终关闭线程池资源

方法四: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("发令枪响!");
         startSignal.countDown();
 
         executor.shutdown();
         executor.awaitTermination(5, TimeUnit.SECONDS);
         System.out.println("\n比赛结束!");
     }
 }

应用场景

  1. 多线程初始化后统一执行:如服务启动时等待所有组件就绪
  2. 并发测试控制:模拟固定数量请求同时发起
  3. 事件驱动编程:等待多个前置条件完成

方法五: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("开发编码", 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
重用性 一次性使用 可重复触发
线程关系 主线程等待子线程 子线程相互等待
典型场景 线程初始化完成后执行 多阶段任务协作

总结对比表

方法 适用场景 核心机制 扩展性
wait/notify 简单生产者-消费者模型 对象锁的等待/通知机制
ReentrantLock+Condition 需要多个条件变量 精细条件控制
Future 异步任务结果获取 任务提交与结果回调
CountDownLatch 多线程等待单一事件 计数器递减触发机制
CyclicBarrier 多阶段任务同步 可重置的屏障计数机制

最佳实践建议

  • 简单同步场景优先使用CountDownLatch
  • 需要结果返回时使用Future
  • 多条件或多阶段场景推荐CyclicBarrier
  • 避免使用过时的Object.wait/notify直接控制
相关推荐
头顶秃成一缕光2 分钟前
若依——基于AI+若依框架的实战项目(实战篇(下))
java·前端·vue.js·elementui·aigc
Three~stone6 分钟前
MySQL学习集--DDL
数据库·sql·学习
Qi妙代码8 分钟前
MYSQL基础
数据库·mysql·oracle
Arbori_2621510 分钟前
Oracle中的UNION原理
数据库·oracle
llzcxdb14 分钟前
【MySQL】理解MySQL的双重缓冲机制:Buffer Pool与Redo Log的协同之道
数据库·mysql
白露与泡影30 分钟前
Java面试题及答案整理( 2025年 4 月最新版,持续更新)
java·开发语言
hunzi_143 分钟前
选择网上购物系统要看几方面?
java·微信小程序·小程序·uni-app·php
SelectDB技术团队1 小时前
Apache Doris 2025 Roadmap:构建 GenAI 时代实时高效统一的数据底座
大数据·数据库·数据仓库·人工智能·ai·数据分析·湖仓一体
冷凝女子1 小时前
【QT】获取文件路径中的文件名,去掉后缀,然后提取文件名中的数字
开发语言·数据库·qt
weixin_435208161 小时前
通过 Markdown 改进 RAG 文档处理
人工智能·python·算法·自然语言处理·面试·nlp·aigc