「Spring Boot:2.2.1」
师傅:徒儿,你过来一下,你说说上一次我按摩,你泡脚,你怎么不等我?
悟纤:师傅,你误会我了,不是我不等你,是我不知道你按摩要按多久呀。
师傅:徒儿,你这是思路没开拓,你咨询大厅经理为师的情况不就可以了嘛。
悟纤:… (你在按摩,我老骚扰你,这个好嘛)
一、你等待我按摩,我等待你泡脚
师傅:我按摩,我按摩之后,你再去泡脚,然后咱们一起回家。
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个小时,大半天时间就没有了。
二、我按摩,你泡脚,各自回家
师傅:咱们最近一次我去按摩,你去泡脚了,然后你不等我就回去了,我按完之后自己孤零零的走在路上。
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=======
悟纤:师傅,你看你按摩舒服去了,我泡完脚,我就无聊的在那等待,也不知道,你啥时候完成呢,你要加时呐,那我又不知道,我不得无聊死了。
三、我按摩,你泡脚,然后等待我一起回家
师傅:徒儿,我发现上一次的按摩经历,我孤零零一个人回家确实是不好,为师,今天要教你一种新的按摩泡脚方式。
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
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的化学反应》
师傅:腰酸背痛,腿抽筋,徒儿,按摩摸摸走起。
我就是我,是颜色不一样的烟火。
我就是我,是与众不同的小苹果。
à悟空学院: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