Java六种异步转同步方案,总有一款适合你

简介: Java六种异步转同步方案,总有一款适合你

一、问题



应用场景


应用中通过框架发送异步命令时,不能立刻返回命令的执行结果,而是异步返回命令的执行结果。


那么,问题来了,针对应用中这种异步调用,能不能像同步调用一样立刻获取到命令的执行结果,如何实现异步转同步?


二、分析


首先,解释下同步和异步


  • 同步,就是发出一个调用时,在没有得到结果之前,该调用就不返回或继续执行后续操作。


  • 异步,当一个异步过程调用发出后,调用者在没有得到结果之前,就可以继续执行后续操作。当这个调用完成后,一般通过状态、通知和回调来通知调用者。


对于异步调用,调用的返回并不受调用者控制。


异步转同步主要实现思路:所有实现原理类似,是在发出调用的线程中进行阻塞等待结果,调用完成后通过回调、设置共享状态或通知进行阻塞状态的解除,继续执行后续操作。


三、实现方法


通常,实现中,不会无限的等待,一般会设定一个超时时间,具体超时时间根据具体场景确定。


下面以回调的方式介绍几种常用实现异步转同步的方法:


1.轮询与休眠重试机制


采用轮询与休眠重试机制,线程将反复在休眠和测试状态条件中之间切换,直到超时或者状态条件满足继续向下执行。这种方式,超时时间控制不准确,sleep时间需要在响应性和CPU使用率之间进行权衡。


private static long MILLIS_OF_WAIT_TIME = 300000L;// 等待时间 5分钟
private final Object lock = new Object();
//3.结果返回后进行回调,解除阻塞
@Override
public void callback(AsynResponse response){
    synchronized(lock){
        //设置状态条件
}
public Result getResult() throws ErrorCodeException {
// 1.异步调用
// 2.阻塞等待异步响应
    long future = System.currentTimeMillis() + MILLIS_OF_WAIT_TIME;
    long remaining = MILLIS_OF_WAIT_TIME;//剩余等待时间
    while(remaining > 0){
        synchronized(lock){
            if(状态条件未满足){
                remaining = future - System.currentTimeMillis();
                Thread.sleep(时间具体场景确定);
            }
        }  
````}
//4.超时或结果正确返回,对结果进行处理
    return result;
}


2.wait/notify


任意一个Java对象,都拥有一组监视器方法(wait、notify、notifyAll等方法),这些方法和synchronized同步关键字配合,可以实现等待/通知模式。但是使用wait/notify,使线程的阻塞/唤醒对线程本身来说是被动的,要准确的控制哪个线程是很困难的,所以是要么随机唤醒等待在条件队列上一个线程(notify),要么唤醒所有的(notifyAll,但是很低效)。当多个线程基于不同条件在同一条件队列上等待时,如果使用notify而不是notifyAll,很容易导致信号丢失的问题,所以必须谨慎使用wait/notify方法。


private static long MILLIS_OF_WAIT_TIME = 300000L;// 等待时间 5分钟
private final Object lock = new Object();
//3.结果返回后进行回调,解除阻塞
@Override
public void callback(AsynResponse response){
    synchronized(lock){
        lock.notifyAll();
}
public Result getResult() throws ErrorCodeException {
  // 1.异步调用
  // 2.阻塞等待异步响应
    long future = System.currentTimeMillis() + MILLIS_OF_WAIT_TIME;
    long remaining = MILLIS_OF_WAIT_TIME;//剩余等待时间
    synchronized(lock){
        while(条件未满足  && remaining > 0){ //被通知后要检查条件
            lock.wait(remaining);
            remaining = future - System.currentTimeMillis();
        }  
````}
  //4.超时或结果正确返回,对结果进行处理
    return result;
}


3.Lock Condition


使用Lock的Condition队列的实现方式和wait/notify方式类似,但是Lock支持多个Condition队列,并且支持等待状态中响应中断。


private static long SECONDS_OF_WAIT_TIME = 300L;// 等待时间 5分钟
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
//3.结果返回后进行回调,解除阻塞
@Override
public void callback(AsynResponse response){
    lock.lock();//这是前提
    try {
        condition.signal();
    }finally {
        lock.unlock();
    }
}
public Result getResult() throws ErrorCodeException {
  // 1.异步调用
  // 2.阻塞等待异步响应
    lock.lock();//这是前提
    try {
        condition.await();
    } catch (InterruptedException e) {
        //TODO
    }finally {
        lock.unlock();
    }
  //4.超时或结果正确返回,对结果进行处理
    return result;
}


4.CountDownLatch


使用CountDownLatch可以实现异步转同步,它好比计数器,在创建实例CountDownLatch对象的时候传入数字,每使用一次 countDown() 方法计数减1,当数字减到0时, await()方法后的代码将可以执行,未到0之前将一直阻塞等待。


private static long SECONDS_OF_WAIT_TIME = 300L;// 等待时间 5分钟
private final CountDownLatch countDownLatch = new CountDownLatch(1);
//3.结果返回后进行回调,解除阻塞
@Override
public void callback(AsynResponse response){
    countDownLatch.countDown();
}
public Result getResult() throws ErrorCodeException {
    // 1.异步调用
    // 2.阻塞等待异步响应
    try {
        countDownLatch.await(SECONDS_OF_WAIT_TIME, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
        //TODO
    }
  //4.超时或结果正确返回,对结果进行处理
    return result;
}


5.CyclicBarrier


让一组线程达到一个屏障(也可以叫同步点)时被阻塞,直到等待最后一个线程到达屏障时,屏障才开门,所有被屏障拦截的线程才会继续执行。

每个线程通过调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前的的线程被阻塞。


private static long SECONDS_OF_WAIT_TIME = 300L;// 等待时间 5分钟
private final CountDownLatch cyclicBarrier= new CyclicBarrier(2);//设置屏障拦截的线程数为2
//3.结果返回后进行回调,解除阻塞
@Override
public void callback(AsynResponse response){
    //我也到达屏障了,可以开门了
    cyclicBarrier.await();
}
public Result getResult() throws ErrorCodeException {
    // 1.异步调用
    // 2.阻塞等待异步响应
    try {
        //我到达屏障了,还没开门,要等一等
        cyclicBarrier.await(SECONDS_OF_WAIT_TIME, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
        //TODO
    }
  //4.超时或结果正确返回,对结果进行处理
    return result;
}


CountDownLatch和CyclicBarrier实现类似,区别是CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset重置,


所以CyclicBarrier能处理更为复杂的业务场景。在异步转同步中,计数器不会重用,所以使用CountDownLatch实现更适合。


6.LockSupport


LockSupport定义了一组公共静态方法,提供了最基本的线程阻塞和唤醒的方法。

private static long NANOS_OF_WAIT_TIME = 300000000L;// 等待时间 5分钟
private final LockSupport lockSupport = new LockSupport();
//3.结果返回后进行回调,解除阻塞
@Override
public void callback(AsynResponse response){
    lockSupport.unpark();
}
public Result getResult() throws ErrorCodeException {
    // 1.异步调用
    // 2.阻塞等待异步响应
    try {
        lockSupport.parkNanos(NANOS_OF_WAIT_TIME);
    } catch (InterruptedException e) {
        //TODO
    }
  //4.超时或结果正确返回,对结果进行处理
    return result;
}

今天多学一点,明天就少说一句求人的话!加油


目录
相关文章
|
8月前
|
机器学习/深度学习 JSON Java
Java调用Python的5种实用方案:从简单到进阶的全场景解析
在机器学习与大数据融合背景下,Java与Python协同开发成为企业常见需求。本文通过真实案例解析5种主流调用方案,涵盖脚本调用到微服务架构,助力开发者根据业务场景选择最优方案,提升开发效率与系统性能。
1978 0
|
9月前
|
Cloud Native 前端开发 Java
WebAssembly 与 Java 结合的跨语言协作方案及性能提升策略研究
本文深入探讨了WebAssembly与Java的结合方式,介绍了编译Java为Wasm模块、在Java中运行Wasm、云原生集成等技术方案,并通过金融分析系统的应用实例展示了其高性能、低延迟、跨平台等优势。结合TeaVM、JWebAssembly、GraalVM、Wasmer Java等工具,帮助开发者提升应用性能与开发效率,适用于Web前端、服务器端及边缘计算等场景。
358 0
|
10月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
425 0
|
8月前
|
缓存 监控 Kubernetes
Java虚拟机内存溢出(Java Heap Space)问题处理方案
综上所述, 解决Java Heap Space溢出需从多角度综合施策; 包括但不限于配置调整、代码审查与优化以及系统设计层面改进; 同样也不能忽视运行期监控与预警设置之重要性; 及早发现潜在风险点并采取相应补救手段至关重要.
974 17
|
8月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
|
10月前
|
缓存 Java 数据库
Java 项目分层架构实操指南及长尾关键词优化方案
本指南详解基于Spring Boot与Spring Cloud的Java微服务分层架构,以用户管理系统为例,涵盖技术选型、核心代码实现、服务治理及部署实践,助力掌握现代化Java企业级开发方案。
436 2
|
11月前
|
安全 Java Docker
Docker 部署 Java 应用实战指南与长尾优化方案
本文详细介绍了Docker容器化部署Java应用的最佳实践。首先阐述了采用多阶段构建和精简JRE的镜像优化技术,可将镜像体积减少60%。其次讲解了资源配置、健康检查、启动优化等容器化关键配置,并演示了Spring Boot微服务的多模块构建与Docker Compose编排方案。最后深入探讨了Kubernetes生产部署、监控日志集成、灰度发布策略以及性能调优和安全加固措施,为Java应用的容器化部署提供了完整的解决方案指南。文章还包含大量可落地的代码示例,涵盖从基础到高级的生产环境实践。
728 3
|
10月前
|
JavaScript Java Go
Go、Node.js、Python、PHP、Java五种语言的直播推流RTMP协议技术实施方案和思路-优雅草卓伊凡
Go、Node.js、Python、PHP、Java五种语言的直播推流RTMP协议技术实施方案和思路-优雅草卓伊凡
804 0
|
数据采集 自然语言处理 JavaScript
Playwright多语言生态:跨Python/Java/.NET的统一采集方案
随着数据采集需求的增加,传统爬虫工具如Selenium、Jsoup等因语言割裂、JS渲染困难及代理兼容性差等问题,难以满足现代网站抓取需求。微软推出的Playwright框架,凭借多语言支持(Python/Java/.NET/Node.js)、统一API接口和优异的JS兼容性,解决了跨语言协作、动态页面解析和身份伪装等痛点。其性能优于Selenium与Puppeteer,在学术数据库(如Scopus)抓取中表现出色。行业应用广泛,涵盖高校科研、大型数据公司及AI初创团队,助力构建高效稳定的爬虫系统。
804 2
Playwright多语言生态:跨Python/Java/.NET的统一采集方案
|
11月前
|
前端开发 数据可视化 Java
开发 JavaFX 与 Java Swing 桌面应用的实用技巧与实践方案
本文介绍了Java桌面应用开发的技术选型与JavaFX实战方案。首先对比了JavaFX和Swing的特点,推荐JavaFX更适合现代UI需求。重点讲解了JavaFX 19+的技术升级,包括模块化开发(module-info.java配置)和响应式UI设计(CSS样式管理)。在数据访问层展示了JDBC 4.3的集成和异步加载实现。高级UI组件部分演示了自定义表格和图表可视化的开发方法。最后介绍了MVVM架构的实现,包括视图模型的数据绑定和FXML控制器的集成,为开发者提供了完整的JavaFX桌面应用开发解决方案。
977 0