我按摩你泡脚,你居然不等我「牛逼的Future」  - 第294篇_生活

 

 

「Spring Boot:2.2.1」

我按摩你泡脚,你居然不等我「牛逼的Future」  - 第294篇_生活_02

师傅:徒儿,你过来一下,你说说上一次我按摩,你泡脚,你怎么不等我?

我按摩你泡脚,你居然不等我「牛逼的Future」  - 第294篇_生活_03

悟纤:师傅,你误会我了,不是我不等你,是我不知道你按摩要按多久呀。

师傅:徒儿,你这是思路没开拓,你咨询大厅经理为师的情况不就可以了嘛。

悟纤:… (你在按摩,我老骚扰你,这个好嘛)

 

一、你等待我按摩,我等待你泡脚

师傅:我按摩,我按摩之后,你再去泡脚,然后咱们一起回家。

    public static void main(String[] args) throws InterruptedException, ExecutionException {

        long startTime = System.currentTimeMillis();

        //师傅:按摩结果
        System.out.println("师傅:开始SPA");
        TimeUnit.SECONDS.sleep(3);
        System.out.println("师傅:结束SPA");


        //悟纤:泡澡
        System.out.println("悟纤:泡脚开始");
        TimeUnit.SECONDS.sleep(2);
        System.out.println("悟纤:泡脚结束");
        System.out.println("悟纤+师傅:go home=======");

        long endTime = System.currentTimeMillis();
        System.out.println("time = "+(endTime-startTime)+"ms");
    }

Console log:

师傅:开始SPA

师傅:结束SPA

悟纤:泡脚开始

悟纤:泡脚结束

悟纤+师傅:go home=======

time = 5007ms

师傅:徒儿,这个太浪费时间了,咱们出去玩一次也不容易,时间很宝贵的,一起出去我3个小时,你2个小时,大半天时间就没有了。

 

二、我按摩,你泡脚,各自回家

我按摩你泡脚,你居然不等我「牛逼的Future」  - 第294篇_生活_04

师傅:咱们最近一次我去按摩,你去泡脚了,然后你不等我就回去了,我按完之后自己孤零零的走在路上。

    public static void main(String[] args) throws InterruptedException, ExecutionException {

        long startTime = System.currentTimeMillis();

        //师傅:按摩
        Runnable runnable =  new Runnable() {
            @Override
            public void run() {
                System.out.println("师傅:开始SPA");
                long time = 3;
                try {
                    TimeUnit.SECONDS.sleep(time);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("师傅:结束SPA,time="+time+"s");
                System.out.println("师傅:go home=======");
            }
        };
        new Thread(runnable).start();


        //悟纤:泡澡
        System.out.println("悟纤:泡澡开始");
        TimeUnit.SECONDS.sleep(2);
        System.out.println("悟纤:泡澡结束,time=2");
        System.out.println("悟纤:go home=======");

        long endTime = System.currentTimeMillis();

        System.out.println("time = "+(endTime-startTime)+"ms");
    }

Console log:

悟纤:泡澡开始

师傅:开始SPA

悟纤:泡澡结束,time=2

悟纤:go home=======

time =2005ms

师傅:结束SPA,time=3s

师傅:go home=======

悟纤:师傅,你看你按摩舒服去了,我泡完脚,我就无聊的在那等待,也不知道,你啥时候完成呢,你要加时呐,那我又不知道,我不得无聊死了。

我按摩你泡脚,你居然不等我「牛逼的Future」  - 第294篇_生活_05

 

三、我按摩,你泡脚,然后等待我一起回家

师傅:徒儿,我发现上一次的按摩经历,我孤零零一个人回家确实是不好,为师,今天要教你一种新的按摩泡脚方式。

 

    public static void main(String[] args) throws InterruptedException, ExecutionException {

        long startTime = System.currentTimeMillis();

        //师傅:按摩结果
        Callable<Long> amCallable = new Callable<Long>() {
            @Override
            public Long call() throws Exception {
                System.out.println("师傅:开始SPA");
                long time = 3;
                TimeUnit.SECONDS.sleep(time);
                System.out.println("师傅:结束SPA,time="+time);
                return time;
            }
        };
        FutureTask<Long> futureTask = new FutureTask<>(amCallable);
        new Thread(futureTask).start();


        //悟纤:泡澡
        System.out.println("悟纤:泡脚开始");
        TimeUnit.SECONDS.sleep(2);
        System.out.println("悟纤:泡脚结束,time=2");
        //询问大厅经理师傅按摩是否结束了,未结束的话,需要等待结果返回。
        long amTime = futureTask.get();
        System.out.println("师傅按摩:"+amTime+"s");
        System.out.println("悟纤+师傅:go home=======");

        long endTime = System.currentTimeMillis();

        System.out.println("time = "+(endTime-startTime)+"ms");
    }

Console log:

师傅:开始SPA

悟纤:泡脚开始

悟纤:泡脚结束,time=2

师傅:结束SPA,time=3

师傅按摩:3s

悟纤+师傅:go home=======

time = 3008ms

师傅:徒儿,你看这种新的按摩方式,就可以一起回家了。

我按摩你泡脚,你居然不等我「牛逼的Future」  - 第294篇_生活_06

 

四、牛逼的Future

4.1 Future缘起

我们通常都是开启一个新的子线程去执行比较耗时的代码,这使用起来非常简单,只需要将耗时的代码封装在Runnable中的run()方法里面,然后调用thread.start()就行。但是我相信很多人有时候都有这样的需求,就是获取子线程运行的结果,比如客户端远程调用服务(耗时服务),我们有需要得到该调用服务返回的结果,这该怎么办呢?很显然子线程运行的run()方法是没有返回值。这个时候Future的作用就发挥出来了。

BTW:当希望能够得到子线程的返回值的话,就需要使用Future来处理。

 

4.2 Future理解

       Future表示异步计算的未来结果 - 这个结果最终将在处理完成后出现在Future中

Future涉及的各个类理解:

(1)FutureTask:new Thread(Runnable) 需要接收一个Runnable的参数,在Future中,FutureTask就是Runnable的子类,所以可以作为Thread的参数直接传入。

(2)Callable:在上面分析了FutureTask就是Runnable的子类,那么run方法就已经被FutureTask进行处理了,但是在这个run方法中,我们又希望调用我们自己的方法,这种情况,我们常用的招式就是使用回调,我们我们定义接口Callable,作为FutureTask的参数进行传入,当FutureTask在调用run方法的时候,执行callable的call()方法。

(3)FutureTask.get():是一个阻塞的方法,直到运行完成之后,返回线程执行的结果。

       所以在编码的过程中,主要通过FutureTask来获取子线程的值,通过实现Callable来执行我们自己的方法,通过new Thread(futureTask)开启一个子线程进行处理。

 

4.3 一个Future的例子

       在微服务项目中,我们会调用不同的微服务进行数据查询,然后数据汇总在进行返回给前端,如果使用同步的方式,那么时间就是调用多个微服务的综合了。如果使用Future,那么就是时间就是最长的线程的时间了。

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        System.out.println("开始查询用户的信息:");
        long startTime = System.currentTimeMillis();
        Callable<String> callable = new Callable<String>() {
            @Override
            public String call() throws Exception {

                //模拟调用远程的服务,所需要的时间
                TimeUnit.SECONDS.sleep(2);
                String rs = "悟纤";//通过用户的id查询出用户的昵称

                return rs;
            }
        };
        FutureTask<String> futureTask = new FutureTask<>(callable);
        new Thread(futureTask).start();

        //模拟当前线程查询数据。
        TimeUnit.SECONDS.sleep(1);
        double rs1 = 1000;//通过当前的用户id,查询账户余额

        //汇总子线程的数据,然后统一进行返回。
        String rs2 = futureTask.get();
        System.out.println("当前账号的昵称为:"+rs2+",账户余额为:"+rs1);

        long endTime = System.currentTimeMillis();
        System.out.println("耗时:"+(endTime-startTime)+"ms");

    }

Console log:

开始查询用户的信息:

当前账号的昵称为:悟纤,账户余额为:1000.0

耗时:2009ms

       如果通过我们同步的代码的话,上面的代码执行时间就会是3s左右,我们优化完之后,就少了1s。

       疑问:虽然代码进行了优化,但是这代码看起来是否还是很不舒服呐,不舒服就对了,如果Future配合上@Async将会产生神奇的化学反应。

下节为你揭晓:《Future love Async的化学反应》

师傅:腰酸背痛,腿抽筋,徒儿,按摩摸摸走起。

我按摩你泡脚,你居然不等我「牛逼的Future」  - 第294篇_生活_07

我就是我,是颜色不一样的烟火。
我就是我,是与众不同的小苹果。

à悟空学院:https://t.cn/Rg3fKJD

 

SpringBoot视频:https://t.cn/R3QepWG

Spring Cloud视频:https://t.cn/R3QeRZc

SpringBoot Shiro视频:https://t.cn/R3QDMbh

SpringBoot交流平台:https://t.cn/R3QDhU0

SpringData和JPA视频:https://t.cn/R1pSojf

SpringSecurity5.0视频:https://t.cn/EwlLjHh

Sharding-JDBC分库分表实战:https://t.cn/E4lpD6e

 

分布式事务解决方案「手写代码」:http://t.cn/AieNUirK