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

 更新时间:2025年02月26日 16:40:32   作者:猿小蔡  
本文介绍了将异步调用转为同步阻塞模式的五种方法:wait/notify、ReentrantLock+Condition、Future、CountDownLatch和CyclicBarrier,每种方法都有其适用场景和核心机制,可以根据具体需求选择合适的方法,需要的朋友可以参考下

异步与同步的核心区别

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

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

方法一:使用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();
     }
 }

关键要点

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

  • 条件判断:

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

  • 方法对比:

    • ​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();
         }
     }
 }

核心特性

  • 多条件支持:

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

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

    • 必须在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()​检查任务是否完成

执行流程

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

方法四: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比赛结束!");
     }
 }

应用场景

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

方法五: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);
         }
     }
 }

核心特性

对比项CountDownLatchCyclicBarrier
重用性一次性使用可重复触发
线程关系主线程等待子线程子线程相互等待
典型场景线程初始化完成后执行多阶段任务协作

总结对比表

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

最佳实践建议:

  • 简单同步场景优先使用CountDownLatch​
  • 需要结果返回时使用Future​
  • 多条件或多阶段场景推荐CyclicBarrier​
  • 避免使用过时的Object.wait/notify​直接控制

以上就是Java中将异步调用转为同步的五种方法的详细内容,更多关于Java异步调用转同步的资料请关注脚本之家其它相关文章!

相关文章

  • Java适配器模式应用之电源适配器功能详解

    Java适配器模式应用之电源适配器功能详解

    这篇文章主要介绍了Java适配器模式应用之电源适配器功能,较为详细的分析了适配器模式的概念、原理及Java基于适配器模式实现电源适配器功能的相关操作技巧与注意事项,需要的朋友可以参考下
    2018-05-05
  • Quarkus集成apollo配置中心

    Quarkus集成apollo配置中心

    这篇文章主要介绍了Quarkus集成apollo配置中心,文中详细的讲解了Quarkus的config构成,以及apollo集成实现,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2022-02-02
  • Java实现一个达达租车系统的步骤详解

    Java实现一个达达租车系统的步骤详解

    这篇文章主要给大家介绍了利用Java实现一个达达租车系统的步骤,文中给出了详细的实现思路和示例代码,并在文末给出了完整的源码供大家学习下载,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-04-04
  • java使用单向链表解决数据存储自定义排序问题

    java使用单向链表解决数据存储自定义排序问题

    本文主要介绍了java使用单向链表解决数据存储自定义排序问题,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • Spring中Service注入多个实现类的方法详解

    Spring中Service注入多个实现类的方法详解

    这篇文章主要介绍了Spring中Service注入多个实现类的方法详解,Spring是一个开源的Java框架,用于构建企业级应用程序,它提供了许多功能,如依赖注入、面向切面编程、数据访问、Web开发等,需要的朋友可以参考下
    2023-07-07
  • mybatis 使用concat 模糊查询方式

    mybatis 使用concat 模糊查询方式

    这篇文章主要介绍了mybatis 使用concat 模糊查询方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • java中使用try-catch-finally一些值得注意的事(必看)

    java中使用try-catch-finally一些值得注意的事(必看)

    下面小编就为大家带来一篇java中使用try-catch-finally一些值得注意的事(必看)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-08-08
  • SpringBoot的自动配置原理解析

    SpringBoot的自动配置原理解析

    这篇文章主要介绍了SpringBoot的自动配置原理解析,SpringBoot的自动配置要从它的启动类@SpringBootApplication说起,点进注解,@Target设置当前注解可以标记在哪,(ElementType.type)表示标注在类上面,需要的朋友可以参考下
    2023-08-08
  • 零基础入门学习——Spring Boot注解(一)

    零基础入门学习——Spring Boot注解(一)

    这篇文章主要介绍了Spring Boot注解学习(一)要点,非常不错,具有参考借鉴价值,需要的朋友参考下吧
    2017-05-05
  • 关于Mybatis中foreach遍历Map的实现示例

    关于Mybatis中foreach遍历Map的实现示例

    这篇文章主要介绍了关于Mybatis中foreach遍历Map的实现示例,MyBatis 是一款优秀的半自动的ORM持久层框架,它支持自定义 SQL、存储过程以及高级映射,需要的朋友可以参考下
    2023-05-05

最新评论