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

2025-02-26 17:50

本文主要是介绍Java中将异步调用转为同步的五种实现方法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Java中将异步调用转为同步的五种实现方法》本文介绍了将异步调用转为同步阻塞模式的五种方法:wait/notify、ReentrantLock+Condition、Future、CountDownL...

异步与同步的核心区别

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

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

方法一:使用wait/notify + synchjsronized

代码示例

 public class ProducerConsumerExample {
     private static final int BUFFER_SIZE = 5;
     priandroidvate 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 ProducerConsjavascriptumerExample();
     
         // 启动生产者和消费者线程
         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) {
   China编程          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 {
        android 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异步调用转同步的资料请关注China编程(www.chinasem.cn)其它相关文章!

这篇关于Java中将异步调用转为同步的五种实现方法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1153548

相关文章

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

SpringBoot多环境配置数据读取方式

《SpringBoot多环境配置数据读取方式》SpringBoot通过环境隔离机制,支持properties/yaml/yml多格式配置,结合@Value、Environment和@Configura... 目录一、多环境配置的核心思路二、3种配置文件格式详解2.1 properties格式(传统格式)1.

Apache Ignite 与 Spring Boot 集成详细指南

《ApacheIgnite与SpringBoot集成详细指南》ApacheIgnite官方指南详解如何通过SpringBootStarter扩展实现自动配置,支持厚/轻客户端模式,简化Ign... 目录 一、背景:为什么需要这个集成? 二、两种集成方式(对应两种客户端模型) 三、方式一:自动配置 Thick

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

Spring WebClient从入门到精通

《SpringWebClient从入门到精通》本文详解SpringWebClient非阻塞响应式特性及优势,涵盖核心API、实战应用与性能优化,对比RestTemplate,为微服务通信提供高效解决... 目录一、WebClient 概述1.1 为什么选择 WebClient?1.2 WebClient 与

Java.lang.InterruptedException被中止异常的原因及解决方案

《Java.lang.InterruptedException被中止异常的原因及解决方案》Java.lang.InterruptedException是线程被中断时抛出的异常,用于协作停止执行,常见于... 目录报错问题报错原因解决方法Java.lang.InterruptedException 是 Jav

深入浅出SpringBoot WebSocket构建实时应用全面指南

《深入浅出SpringBootWebSocket构建实时应用全面指南》WebSocket是一种在单个TCP连接上进行全双工通信的协议,这篇文章主要为大家详细介绍了SpringBoot如何集成WebS... 目录前言为什么需要 WebSocketWebSocket 是什么Spring Boot 如何简化 We

java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)

《java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)》:本文主要介绍java中pdf模版填充表单踩坑的相关资料,OpenPDF、iText、PDFBox是三... 目录准备Pdf模版方法1:itextpdf7填充表单(1)加入依赖(2)代码(3)遇到的问题方法2:pd

Java Stream流之GroupBy的用法及应用场景

《JavaStream流之GroupBy的用法及应用场景》本教程将详细介绍如何在Java中使用Stream流的groupby方法,包括基本用法和一些常见的实际应用场景,感兴趣的朋友一起看看吧... 目录Java Stream流之GroupBy的用法1. 前言2. 基础概念什么是 GroupBy?Stream

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统