设为首页 加入收藏

TOP

Java并发编程 - Callable、Future和FutureTask的实现
2019-02-10 22:08:40 】 浏览:45
Tags:Java 并发 编程 Callable Future FutureTask 实现

启动线程执行任务,如果需要在任务执行完毕之后得到任务执行结果,可以使用从Java 1.5开始提供的Callable和Future
下面就分析一下Callable、Future以及FutureTask的具体实现及使用方法
源码分析基于JDK 1.7


java.lang.Runnable是一个接口,只有一个run()方法


run()方法的返回值是void,故在执行完任务后无法返回任何结果


Callable是java.util.concurrent包下的,也是一个接口,也只有一个call()方法,类似于java.lang.Runnable的run()方法,实现Callable接口的类和实现Runnable接口的类都是可以被其它线程执行的任务


可以看到call()方法是有返回值的,可以将执行的结果返回


Future是java.util.concurrent包下的一个接口,代表着一个异步计算的结果,可以通过get()获取线程执行的返回值,cancel()取消任务执行,isCancelled()isDone()获得任务执行的情况


尝试取消任务的执行,取消成功返回true,取消失败返回false
mayInterruptIfRunning表示是否允许中断正在执行的任务
1、如果任务还未开始,cancel返回true,且任务永远不会被执行
2、如果任务正在执行,根据mayInterruptIfRunning的值判断是否需要中断执行中的任务,且如果mayInterruptIfRunning为true,会调用中断逻辑,返回true;如果mayInterruptIfRunning为false,不会调用线程中断,只是将任务取消
3、如果任务结束(可能是正常完成、异常终止、被取消),返回false
4、如果cancel()操作返回true,后续调用isDone()、isCancelled()都返回true


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


表示任务是否已经完成,则返回true,注意:正常完成、异常 或 取消操作都代表任务完成


get()用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回
get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内还没获取到结果,会抛出TimeoutException


因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask


FutureTask实现了RunnableFuture接口,那么RunnableFuture又是什么呢?


RunnableFuture接口继承了RunnableFuture,所以它既是一个可以让线程执行的Runnable任务,又是一个可以获取Callable返回值的Future


state 是任务的运行状态


callable 是线程执行的有返回值的任务
outcome 是任务执行后的结果或异常
waiters 表示等待获取结果的阻塞线程,链表结构,后等待线程的会排在链表前面


FutureTask有两个构造方法:
FutureTask(Callable callable)


构造方法参数是Callable定义的任务,并将state置为NEW,只有当state为NEW时,callable才能被执行


FutureTask(Runnable runnable, V result)


参数为Runnable和带泛型的result对象,由于Runnable本身是没有返回值的,故线程的执行结果通过result返回
可以看到通过runnable和result封装了个Callable,实际上是new RunnableAdapter<T>(task, result),这个Adapter适配器将Runnable和result转换成Callable,并返回result


线程运行时真正执行的方法,Callable.call()会在其中执行,并包含设置返回值或异常的逻辑


1、任务执行状态不是NEW,直接返回;将runner属性从null->当前线程不成功,??接返回
2、调用call()方法,调用成功,使用set()设置返回值
3、调用过程发生异常,使用setException()保存异常


 


set() 和 setException()


set()setException()的实现基本一样,都是先将任务运行状态从NEW->COMPLETING,分别设置返回值或异常给outcome,再将状态分别置为NORMAL和EXCEPTIONAL,最后调用finishCompletion()依次唤醒等待获取结果的阻塞线程


 


finishCompletion()实现


1、执行FutureTask类的get方法时,会把主线程封装成WaitNode节点并保存在waiters链表中
2、FutureTask任务执行完成后,通过UNSAFE设置waiters的值为null,并通过LockSupport.unpark方法依次唤醒等待获取结果的线程


get()方法有两个实现,一个是一直等待获取结果,直到任务执行完;一个是等待指定时间,超时后任务还未完成会上抛TimeoutException


内部通过awaitDone()对主线程进行阻塞,具体实现如下:


1、判断主线程是否被中断,如果被中断,将当前WaitNode节点从waiters链表中删除,并上抛InterruptedException
2、如果任务已经完成(可能是正常完成、异常、中断),直接返回(即还没有开始等待,任务已经完成了,就返回了)
3、如果任务正在完成,让出CPU资源,等待state变成NORMAL或EXCEPTIONAL
4、如果任务没有被中断,也没有完成,new WaitNode()
5、如果任务没有被中断,也没有完成,也创建了WaitNode,使用UNSAFE.CAS()操作将WaitNode加入waiters链表
6、所有准备工作完毕,通过LockSupport的park或parkNanos挂起线程


 


WaitNode就是一个简单的链表节点,记录这等待的线程和下一个WaitNode


 


1、如果任务不是运行状态,直接返回false失败
2、如果mayInterruptIfRunning==true,中断运行中的任务,使用CAS操作将状态NEW-->INTERRUPTING,再调用runner.interrupt(),最后将状态置为INTERRUPTED
3、如果mayInterruptIfRunning==false,将任务置为CANCELLED取消状态
4、调用finishCompletion()依次唤醒等待获取结果的线程,返回true取消成功


运行结果:


如果只是想控制在某些情况下可以将任务取消,可以使用Future<?> future = executor.submit(runnable),这样返回结果肯定为null,但可以使用future.cancel()取消任务执行


1、有了Runnable,为什么还需要Callable,它们的区别是什么?


2、Future.get()是如何获取线程返回值的?


3、Future.cancel()真的能取消任务的执行吗?



编程开发网
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Java线程池ThreadPoolExecutor实.. 下一篇Java设计模式之适配器模式

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

array(4) { ["type"]=> int(8) ["message"]=> string(24) "Undefined variable: jobs" ["file"]=> string(32) "/mnt/wp/cppentry/do/bencandy.php" ["line"]=> int(214) }