Java异步非阻塞编程的几种方式总结

 更新时间:2021年06月29日 14:25:43   作者:平静不绝望  
这篇文章主要介绍了Java异步非阻塞编程的几种方式总结,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

1 服务端执行,最简单的同步调用方式:

  • 缺陷:
  • 服务端响应之前,IO会阻塞在:
  • java.net.SocketInputStream#socketRead0 的native方法上:

2 JDK NIO & Future java 1.5之后

  • 优点:主线程可以不用等待IO响应,可以去做点其他的,比如说再发送一个IO请求,可以等到一起返回;
  • 缺点:主线程在等待结果返回过程中依然需要等待,没有根本解决此问题;

3 使用Callback回调方式

  • 优点:主线程完成发送请求后,再也不用关心这个逻辑,去执行其他的逻辑;整个过程中已经没有线程阻塞;如 使用nio的EventLoopGroup中的线程执行完所有逻辑;
  • 缺点:回调地狱;Callback hell ;代码可读性低、编写费劲、容易出错

4 JDK 1.8 CompletableFuture

  • 优点:解决Callback Hell的问题,JDK 1.8中提供了CompletableFuture;每一个IO操作,均可以封装为独立的CompletableFuture,从而避免回调地狱。
  • 实现:将逆Callback逻辑,封装成一个独立的CompletableFuture,当异步线程回调时,调用 future.complete(T) ,将结果封装;thenCompose衔接,whenComplete输出;
  • 小结:这样一来,就完美解决回调地狱问题,在主的逻辑中,看起来像是在同步的进行编码。

5 源码举例 测试+结果

 
import java.util.concurrent.CompletableFuture; 
public class CompletableFutureTest { 
    private static CompletableFuture<String> invokeAFuture(String rawASource){
        CompletableFuture<String> future = new CompletableFuture<>();
        System.out.println("pre-do-invokeA");
        try {
            Thread.sleep(1000);
            future.complete("invokeA "+rawASource+" result = skip");
        }catch (Exception e){
 
        }
        return future;
    }
 
    private static CompletableFuture<String> invokeBFuture(String rawAResult){
        CompletableFuture<String> future = new CompletableFuture<>();
        System.out.println("pre-do-invokeB");
        try {
            Thread.sleep(1000);
            future.complete("after A done result = "+rawAResult+", then invokeB result = success");
        }catch (Exception e){
 
        }
        return future;
    }
 
    public static void main(String[] args) {
        invokeAFuture("加油").thenCompose(aResult-> invokeBFuture(aResult)).whenComplete((resultB, throwable) ->{
            if(throwable != null){
                throwable.printStackTrace();
                return;
            }
            System.out.println(resultB);
        });
    }
    
     public static void main(String[] args) {
        invokeAFuture("加油").thenCompose(CompletableFutureTest::invokeBFuture).whenComplete((resultB, throwable) ->{
            if(throwable != null){
                throwable.printStackTrace();
                return;
            }
            System.out.println(resultB);
        });
    }
} 
pre-do-invokeA
pre-do-invokeB
after A done result = invokeA 加油 result = skip, then invokeB result = success

6 小结:

  • 1 尝试使用异步编程方式;
  • 2 剖析内部实现原理;
  • 3 java9 juc 包有了更抽象的flow处理方式;

Java 异步编程最佳实践

什么是异步?为什么要用它?

异步编程提供了一个非阻塞的,事件驱动的编程模型。 这种编程模型利用系统中多核执行任务来提供并行,因此提供了应用的吞吐率。此处吞吐率是指在单位时间内所做任务的数量。 在这种编程方式下, 一个工作单元将独立于主应用线程而执行, 并且会将它的状态通知调用线程:成功,处理中或者失败。

我们需要异步来消除阻塞模型。其实异步编程模型可以使用同样的线程来处理多个请求, 这些请求不会阻塞这个线程。想象一个应用正在使用的线程正在执行任务, 然后等待任务完成才进行下一步。 log框架就是一个很好的例子:典型地你想将异常和错误日志记录到一个目标中, 比如文件,数据库或者其它类似地方。你不会让你的程序等待日志写完才执行,否则程序的响应就会受到影响。 相反,如果对log框架的调用是异步地,应用就可以并发执行其它任务而无需等待。这是一个非阻塞执行的例子。

为了在Java中实现异步,你需要使用Future 和 FutureTask, 它们位于java.util.concurrent包下. Future是一个接口而FutureTask是它的一个实现类。实际上,如果在你的代码中使用Future, 你的异步任务会立即执行, 并且调用线程可以得到结果promise。

该做和不该做的

为了方便测试,你应该在代码中将功能从多线程中隔离出来。当在Java中编写异步代码时,你应该遵循异步模型,这样调用线程就不会被阻塞。

注意构造函数不能是异步的,你不应该在构造函数中调用异步方法。当任务互相不依赖时异步方式尤其有用。当调用任务依赖被调用任务时不应该使用异步(译者按:这对异步来说无意义,因为业务上调用线程被阻塞了).

你应该在异步方法中处理异常. 你不应该为长时间的task实现异常. 一个长时间运行的任务,如果异步执行的话, 可能会比同步执行耗费更长的时间, 因为运行时要为异步执行的方法执行线程上下文的切换, 线程状态的存储等. 你也应该注意同步的异常和异步的异常有所不同。

同步异常暗示 每次程序执行到那个程序特殊状态时就会抛出异常;异步异常的跟踪则困难的多。

所以同步和异步异常暗示同步或异步代码可能抛出异常

synchronous and asynchronous exceptions imply synchronous or asynchronous code in your program that might raise exceptions.

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • spring @Conditional的使用与扩展源码分析

    spring @Conditional的使用与扩展源码分析

    这篇文章主要介绍了spring @Conditional的使用与扩展,这里需要注意如果Condition返回的是false,那么spirng就不会对方法或类进行解析,具体源码分析跟随小编一起看看吧
    2022-03-03
  • java与微信小程序实现websocket长连接

    java与微信小程序实现websocket长连接

    这篇文章主要为大家详细介绍了java与微信小程序实现websocket长连接,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-05-05
  • Spring Boot中@Conditional注解介绍

    Spring Boot中@Conditional注解介绍

    @Conditional表示仅当所有指定条件都匹配时,组件才有资格注册。该@Conditional注释可以在以下任一方式使用:作为任何@Bean方法的方法级注释、作为任何类的直接或间接注释的类型级别注释@Component,包括@Configuration类、作为元注释,目的是组成自定义构造型注释
    2022-09-09
  • Java代理模式实例详解【静态代理与动态代理】

    Java代理模式实例详解【静态代理与动态代理】

    这篇文章主要介绍了Java代理模式,结合实例形式详细分析了java静态代理与动态代理模式相关概念、原理、操作技巧与注意事项,需要的朋友可以参考下
    2019-09-09
  • MyBatis批量插入(insert)数据操作

    MyBatis批量插入(insert)数据操作

    本文给大家分享MyBatis批量插入(insert)数据操作知识,非常不错,具有参考借鉴价值,感兴趣的朋友一起学习吧
    2016-06-06
  • SpringBoot Actuator未授权访问漏洞解决方案

    SpringBoot Actuator未授权访问漏洞解决方案

    工作的时候遇到过提示Spring Boot后端存在Actuator未授权访问漏洞,网上有很多详细的解释文章,在这里做一个简单的总结、介绍和分享,需要的朋友可以参考下
    2023-09-09
  • Java 实战项目之在线点餐系统的实现流程

    Java 实战项目之在线点餐系统的实现流程

    读万卷书不如行万里路,只学书上的理论是远远不够的,只有在实战中才能获得能力的提升,本篇文章手把手带你用java+SSM+jsp+mysql+maven实现一个在线点餐系统,大家可以在过程中查缺补漏,提升水平
    2021-11-11
  • 浅析java中ArrayList与Vector的区别以及HashMap与Hashtable的区别

    浅析java中ArrayList与Vector的区别以及HashMap与Hashtable的区别

    以下是对java中ArrayList与Vector的区别以及HashMap与Hashtable的区别进行了详细的解析。需要的朋友可以过来参考下
    2013-08-08
  • 深度分析java dump文件

    深度分析java dump文件

    java内存dump是jvm运行时内存的一份快照,利用它可以分析是否存在内存浪费,可以检查内存管理是否合理,当发生OOM的时候,可以找出问题的原因。那么dump文件的内容是什么样的呢?
    2021-05-05
  • Java语言Lang包下常用的工具类介绍

    Java语言Lang包下常用的工具类介绍

    这篇文章主要介绍了Java语言Lang包下常用的工具类介绍,次奥变觉得挺不错的,这里分享给大家,需要的朋友可以参考下。
    2017-10-10

最新评论