1.本文默认,都是已使用过FutreTask类以及线程池
1.FutureTask 是什么?
我们可以看到Futuetask 是继承了RunnableFuture接口,然后RunnableFuture继承了Future和Runnable接口,Runnable接口就不多说了,而这个Future接口是一个异步计算类接口,它提供了获取结果、判断是否执行完成、取消任务等接口,所以FutureTask其实就是一个支持异步任务获取结果的一个异步任务类,其实内部还使用一个callable接口这个接口是Runnable接口的进阶版它也是创建线程的一种方式只是他支持返回结果以及抛出异常,FutureTask它支持callable接口以及Runnable接口提交任务,内部使用了一个RunnableAdapter接口做适配.
2.FutureTask如何使用?
- 提交的是Runnable 的任务
1 | typescript复制代码FutureTask<String> task = new FutureTask<String>(new Runnable() { |
- 提交的是Callable的任务
1 | typescript复制代码FutureTask<String> callableTask = new FutureTask<String>(new Callable<String>() { |
- 基于函数式编程提交任务(其实就是callable)
1 | arduino复制代码FutureTask<String> functionTask = new FutureTask<String>(()->{ |
最后把任务提交给线程池处理即可
3.FutureTask源码分析
3.1 FutureTask构建函数以及相关变量
FutureTask类是一个一个stat变量用来记录当前任务执行的状态:新建、正在计算(完成的中间)、完成、取消、正在中断、中断等几个状态。
构造函数有两个,一个是基于Callable接口提交任务,一个基于Runnable,但是FutureTask的任务是callable所以,它需要通过适配器类进行适配。
1 | java复制代码全局状态,用来判断当前任务处于那个阶段 |
3.2 获取任务结果的get方法
FutureTask的get()获取结果的方法,有两个一个是不指定时间获取,一直阻塞到任务执行完成,第二个是可以指定时间获取结果,如果获取不到就会抛出异常信息和返回异常信息。
get方法做的操作主要有几个:
- 调用awaitDone方法进行获取任务状态,该方法主要进行几个判断:
- 判断线程是否已经中断,如果在一九被中断,则从等待线程链表节点中移除,然后抛出中断移除
- 判断当前任务状态,如果状态是已经完成状态,则把当前线程节点的线程置空,然后返回任务状态
- 如果当前结果正在计算中(完成前的中间状态),则让当前线程进行礼让(让出cpu)
- 如果当前节点是null ,则构建一个waiterNode节点(默认是当前线程)
- 如果节点没入队,则把waiteNode节点入队
- 如果等待有时间限制,则判断是否超过时间,如果超过则移除节点,返回状态,否则通过LockSupport挂起线程(分有时间限制和没时间限制)
- 调用report方法处理结果,该方法通过判断当前任务状态处理什么状态返回返回不同的结果。
下面源码分析:
1 | java复制代码/** |
3.3 任务取消方法Cancel
FutureTask 取消任务方法主要是做了几个操作:
- 如果任务状态新建并且通过cas修改任务状态为中断或者取消状态失败则直接返回,如果修改成功,则进行一个中断处理,然后通过Unsafe方法 需要任务状态设为中断状态
- 最后调用finishCompletion方法进行收尾工作,其实就是把线程等待节点处理。
- finishCompletion方法主要是通过Unsafe方法把waiteNode节点轮流置空和把线程唤醒, 处理完成后,会回调一个done方法,这个方法是个钩子方法,让子类去做扩展操作的,在guava中ListenableFuture中就通过这个done方法执行监听器执行链的
下面是源码分析:
1 | typescript复制代码//用于取消任务执行,是否支持中断取消 |
3.4 FutureTask的run方法以及Set方法
FutureTask的run方法是重新了接口中的run方法,线程池中创建线程出来执行任务就是调用run方法去执行,run方法的主要操作有几个
- 判断执行任务的线程状态是否是当前线程以及当前任务的状态,通过cas把当前线程设置为执行任务线程,如果设置失败证明线程已经被其他线程处理了,或者当前任务状态表示新建也是表示已经有其他线程处理了。
- 如果是为执行的任务,则再次判断状态已经任务callable是否合法,如果校验通过,则调用callable的call方法执行热为奴,如果出现异常则调用setException方法把异常处理
- 如果正常执行完成,则通过set方法设置结构到全局结构中。
- 最后把执行任务线程置空,避免重复执行任务(因为是并发执行),,然后会调用一个handlePOossibleCancellationinterrupt来判断是否被其他线程取消任务修改状态
set方法主要是用来处理结果的,一个是通过判断当前任务状态,如果状态是完成的中间状态的着把结果赋值给全局任务结果outCome ,然后把状态修改为完成,最后调用finishCompletion方法处理收尾工作。 - runAndReset方法和run方法类似,只这个方法是处理定时任务线程池执行任务才会调用这个,这个方法执行完成后会把任务状态设置为新建状态,为下一次定时执行做准备。
下面是源码分析:
1 | typescript复制代码 //该方法是当线程执行完run方法之后会调用set方法使用cas修改状态、设置结果,处理善后工作. |
3.5 WaitNode内部类及remodeWaitNode方法
waitNode类是一个简单的单链表,主要是用来存储一个堆栈中的等待线程,而waitNode方法主要是进行节点移除操作。
1 | ini复制代码 /** |
- 总结
FuturTask 是一个可以获取任务结果和处理移除的异步任务类,它需要结合线程池来使用,任务的执行入口其实是在线程池中,线程池通过调用FutureTask实现的run方法,然后FutureTask同Unsafe类处理任务状态以及任务线程来保证线程只有一次,同时还预留了扩展接口done,让我们可以对FutureTask类进行扩展,而guava中的ListenableFutre就是基于FutureTask的done接口进行了扩展实现了future的监听器。
本文转载自: 掘金