【IO】Java异步非阻塞编程的几种方式

345 阅读2分钟

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处理方式;