JUC——异步回调(CompletableFuture)

概述

何为异步?

在了解这个问题之前 先来看看什么是同步。

线程发出一个请求,在这个请求的相应没回来之前,此线程啥也不能做,只能等着。

就好比你去食堂吃饭,要排队。


再来看异步:

线程发出一个请求,该请求会进去消息队列去替线程排队,该线程可以继续处理其他事情。

就好比你去银行办理业务,去了先取号,号替你排队。


显然,异步看起来效率更高。

同步的作用,在保证业务逻辑正确性的同时,也限制了执行效率。

  • 这一点没什么不对,因为逻辑不对,程序效率再高又有什么意义呢?

但,有时候同步的限制力度会超出业务逻辑本身。

我们要做的,就是 最大程度的异步 和 最精准的同步。


场景1——不需要返回值的

一些不需要返回值的请求,自然可以达到完全异步。

给个例子感受一下:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class Future_Demo_0 {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("runAsync");
        });
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main");
        long end = System.currentTimeMillis();
        System.out.println("time:"+(end-start));
    }
}

结果:

main
time:1056
runAsync

在主线程和子线程里 我们都让其休眠了一秒钟,但从结果看来,主 、子 线程应该是一块儿休眠的。各睡各的,并行操作(多CPU核下)。

场景2——需要借助响应的

主线程需要得到子线程的处理结果才能继续完成某个事情。

同样给个例子帮助治疗一下:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class FutureDemo_1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "supplyAsync";
        });

        System.out.println("模拟其他任务1——start...");
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("模拟其他任务1——end...");

        if (!future.isDone()){//判断异步任务是否执行完毕
            System.out.println("异步任务还没完成,不能执行异步任务,请耐心等待...");
        }
        System.out.println("获得异步任务返回值: "+future.get());//get 方法会阻塞

        System.out.println("异步任务返回值到位,开始执行依赖任务——start...");
        TimeUnit.SECONDS.sleep(1);
        System.out.println("异步任务返回值到位,完成执行依赖任务——end...");

        long end = System.currentTimeMillis();
        System.out.println("time:"+(end-start));
    }
}

结果:

模拟其他任务1——start…
模拟其他任务1——end…
异步任务还没完成,不能执行异步任务,请耐心等待…
获得异步任务返回值: supplyAsync
异步任务返回值到位,开始执行依赖任务——start…
异步任务返回值到位,完成执行依赖任务——end…
time:4045

上述情况:按同步的处理思想的话:异步任务3秒 任务一1秒 依赖任务 2秒。

一共要花费 6 秒才行。

这里只花费了4秒。

其实像这样的简单业务用 Thread.yield(); 也能实现,但如果业务比较复杂的话,可能就有点窒息了。

上一篇:看完,你也能用多线程让接口提升5倍速!


下一篇:基础篇:异步编程不会?我教你啊!CompeletableFuture