前言:
这次来学习一下关于Java并发当中非常重要的一个模式----修饰模式,这种模式或多或少都有所耳闻,这种模式是从JDK1.5开始引入到并发包中,它从本质上是用来解决的根本的问题在于可以让一个任务在异步执行,而任务执行完之后我们可以在未来的某一个时间点上去获取到这个任务执行的结果,不管这个任务是执行失败或成功,举一个粟子:比如我们要完成一件任务,正常情况是要等到这个任务执行完之后才能拿到任务执行的结果【单线程顺序执行逻辑】,而如果使用Future模式之后,则执行任务可以交由别的线程来执行,而我们可继续往下执行自己的事件,当在未来的某个时间点又能拿到这个任务的结果,其实也就是并行的方式,可能有的人说这不就是线程嘛,是的,但是普通的线程我们要等其执行完不得使用join()么,但是它会阻塞我们的继续往下执行。废话了这么多,接下来则来了解一下这种Future横式,刚好前几天有个后端的同事问到了这块的问题,当时我回答得也是模棱两可的,正好弥补一下相关的知识点。
Future模式了解:
类文档阅读:
首先来查看一下这个类:
它是一个接口,从jdk1.5提出来:
这个类名也取得比较贴切,代表未来的意思,也就是在未来的某个时候会返回执行的结果,好,先来读一读它的文档描述,对它先有一个整体的认识:
了解它核心的方法:
概读了它的说明之后下面来看一下它里面所定义的方法:
其实它里面最核心的方法是剩下的这两个方法:
读一下这两个get()方法有啥区别,其实一看就能猜出来,第一个是不带超时的,第二个是带超时的,确认一下:
FutureTask了解:
对于Future而言它是一个接口,要使用还得用具体实现类,很明显如这小标题所示,FutureTask就是Future的一个具体实现类之一,也是我们平常都或多或少能见到的,所以接一下了解一下它:
瞅一下RunnableFuture呗:
接下来再来看一下FutureTask的重要方法:
对于上面两个构造的使用场景其实也很好理解,当我们想要执行一个任务但是不需要得到返回结果,那么用Runnable的构造既可,而如果需要有返回结果当然就使用Callable的构造喽,看一下Callable接口的定义:
再简单看一下Future中最重要的两个方法的实现:
具体逻辑就不细看了,另外由于它实现了Runnable接口,很显然这里面会重写run()方法,瞅一下:
不是有一个构造是传的Runnable的么,咋最终执行的是Callable呢?再来回看一下就知道了:
嗯,大致对于Future和FutureTask有了初步了解之后,下面则来使用一下。
实践:
1、先来定义Callable对象,最终要执行的就是它:
package com.javacurrency.test7;
import java.util.Random;
import java.util.concurrent.Callable;
public class MyTest1 {
public static void main(String[] args) {
Callable<Integer> callable = () -> {
System.out.println("pre execution");
int randomNumber = new Random().nextInt(500);
System.out.println("post exection");
return randomNumber;
};
}
}
2、构建FutureTask任务:
3、启动一个线程来执行这个FutureTask任务:
4、获取FutureTask执行的结果:
5、运行程序,并分析执行流程:
也有可能输出它:
简单分析一下,比较好理解:
上面这个程序貌似没能看到Future.get()是有阻塞功能的,所以下面改造一下,加个延时来感受一下:
下面看一下运行效果:
是不是很明显主线程在等FutureTask子线程的执行结果阻塞了,对于Future.get()还有另一个版本:
那咱们也来试试,很显然这个就不会死等着FutureTask的返回结果了,过了超时时间就不等了,改一下:
运行:
抛异常了:
因为主线程的异常不会干扰到FutureTask这个子线程的执行的,说实话实际工作中还没用过FutureTask,所以在学习了之后期待在未来工作中能实践一下~~
关注个人公众号,获得实时推送