java 异步得到函数返回值_Java并发编程十二 异步之Callable、Future和FutureTask

Callable、Future和FutureTask是jdk1.5,java.util.concurrent包提供的异步框架,这里再讲一下什么是异步。

异步是指起多个线程,多个线程之间互不干扰,各自执行各自的任务,在代码中可能书写顺序有先有后,但有可能写在后面的线程会比写在前面的线程先执行任务,异步对应并行的概念,常见的异步操作有线程池、Callable、completeFuture等。

同步是多线程里针对竞争现象的一个处理,竞争是指同一时刻有多个线程访问临界资源,可能会引发程序执行错误的结果,同步就是保证某一时刻仅能有一个线程访问临界资源,同步对应串行的概念,常见的同步操作有synchronized关键字、Lock、线程变量、Atomic原子类等。

什么时候需要异步?比如要执行一个任务,该任务执行完后会返回一个结果供其他任务使用,但是该任务很耗时,如果我们把程序设计成串行执行,先执行这个耗时任务,等他结束后再把执行结果给下一个任务使用,这样会耗时,且在这个任务执行期间,其他任务都被阻塞了。我们可以把程序设计成异步,起一个线程执行这个耗时任务,此外主线程做其他事情,等这个耗时任务执行完毕后,主线程再把结果拿到,使用这个结果继续做其他事情,这样在这个耗时任务执行的过程中,主线程可以去做其他事情而不是等他执行完,这样效率会很高,因此异步编程在提高并发量上使用广泛。

今天介绍的三个类/接口,与线程池组合使用,组成了一套java提供的异步框架。

1、Callable接口

先看Callable接口的源码:

@FunctionalInterface

首先是注解是函数式接口,意味着我们可以用lambda表达式更简洁地使用它。Callable是个泛型接口,只有一个方法call,该方法返回类型就是传递进来的V类型。call方法还支持抛出异常.

与Callable对应的是Runnable接口,实现了这两个接口的类都可以当做线程任务递交给线程池执行,Runnable接口的源码如下:

@FunctionalInterface

既然实现了这两个接口的类都可以当做线程任务,这两个接口有啥区别呢?

  1. Runnable接口是java1.1就有的,Callable接口是java1.5才有的,可以认为Callable接口是升级版的Runnable接口;
  2. Runnable接口里线程任务是在run方法里写的,Callable接口里线程任务是在call方法里写;
  3. Callable接口的任务执行后会有返回值,Runnable接口的任务无返回值(void);
  4. call方法支持抛出异常,run方法不可以;
  5. 加入线程池运行,Runnable使用ExecutorService的execute方法,Callable使用ExecutorService的submit方法;
  6. 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。Future对象封装了检查计算是否完成、检索计算的结果的方法,而Runnable接口没有。

第五点提到了Callable使用ExecutorService的submit方法,这里看一下ExecutorService接口里的submit方法的重载情况:

<

常用的是第一个和第三个,这两个方法分别提交实现了Callable接口的类和实现了Runnable接口的类作为线程任务,返回异步计算结果Future,Future里面封装了一些实用方法可以对异步计算结果进行进一步处理。

2、Future

Future是一个接口,封装了异步计算的结果,源码如下:

public 

下面对这五个方法介绍:

(1)booleancancel(boolean mayInterruptIfRunning)

参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。(讲道理这我都没细看,用到时再看)。

(2)boolean isCancelled()

isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。

(3)boolean isDone()

isDone方法表示任务是否已经完成,若任务完成,则返回true;

(4)V get() throws InterruptedException, ExecutionException

get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;

(5) V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException

get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。

注意两个get方法都会抛出异常。

3、FutureTask

Future是一个接口,FutureTask是Future接口的唯一实现类,一般用的时候向上转型,使用Future。源码如下:

public 

FutureTask类实现的是RunnableFuture<V>接口,该接口的源码如下:

public 

该接口继承了Runnable接口和Future接口,因此FutureTask类既可以当做线程任务递交给线程池执行,又能当Callable任务的计算结果。

4、使用示例

4.1 Callable + Future

实现Callable接口创建一个异步任务的类,在主线程中起一个线程池执行异步任务,然后在主线程里拿到异步任务的返回结果。

package 

执行结果如下:

异步线程任务执行结果 1814400
检查异步线程任务是否执行完毕 true

4.2 Callable + FutureTask

要做的事情跟4.1一样。

package 

执行结果与4.1一样。

要注意FutureTask与Future的使用区别,4.2里的FutureTask对象需要一个实现了Callabel接口的类的对象初始化,FutureTask对象向线程池submit的时候没有Future<V>的返回值,而Future有,此外由于FutureTask对象既可以当做线程任务递交给线程池执行,又可以当做Future拿到异步任务的返回结果,因此 FutureTask对象可以直接get得到异步任务的执行结果。

Future与FutureTask的区别:

  1. Future是一个接口,FutureTask是一个实现类;
  2. 使用Future初始化一个异步任务结果一般需要搭配线程池的submit,且submit方法有返回值;而初始化一个FutureTask对象需要传入一个实现了Callable接口的类的对象,直接将FutureTask对象submit给线程池,无返回值;
  3. Future + Callable获取结果需要Future对象的get,而FutureTask获取结果直接用FutureTask对象的get方法即可。

参考

Java并发编程:Callable、Future和FutureTask - 平凡希 - 博客园​www.cnblogs.com
2bd8132a584cc1f7720af292f5468832.png
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值