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直接控制
相关推荐
Lin_Coder_11 分钟前
Python入门 — 类
开发语言·python
局外人_Jia32 分钟前
【C# 变量字符串还原转义字符】
linux·数据库·windows·正则表达式·c#·字符串·indexof
楠枬36 分钟前
网页五子棋——项目测试
java·功能测试·spring
数据知道39 分钟前
python中httpx库的详细使用及案例
开发语言·爬虫·python·httpx
Gauss松鼠会1 小时前
GaussDB 闪回恢复技术详解与应用实践
数据库·sql·哈希算法·database·gaussdb
leegong231111 小时前
什么是 OCP 数据库专家
数据库·oracle
那雨倾城1 小时前
OpenCV的形态学操作
图像处理·python·opencv·计算机视觉
liuweidong08021 小时前
【Pandas】pandas Series add_prefix
python·机器学习·pandas
梦游钓鱼1 小时前
使用virtualenv遇到的问题,工具冲突
linux·python·virtualenv
@_猿来如此1 小时前
基于django图书信息管理系统的搭建(增删改查)
后端·python·django