FutureTask可用于异步获取执行结果或取消执行任务的场景。通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过FutureTask的get方法异步获取执行结果,因此,FutureTask非常适合用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。另外,FutureTask还可以确保即使调用了多次run方法,它都只会执行一次Runnable或者Callable任务,或者通过cancel取消FutureTask的执行等。

Callable中有一个call()函数,但是call()函数有返回值,而Runnable的run()函数不能将结果返回给客户程序。Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果、设置结果操作。get方法会阻塞,直到任务返回结果。

FutureTask是一个RunnableFuture<V>,RunnableFuture实现了Runnbale又实现了Futrue<V>这两个接口。

FutureTask实现Runnable,所以能丢到Thread执行。

FutureTask实现Runnable,所以能丢给ExcecuteService线程池执行,ExcecuteService线程池也是吧任务丢到Thread里面去的。

可以直接通过get()函数获取执行结果,该函数会阻塞,直到结果返回。

Callable 和 Future接口的区别
Callable规定的方法是call(),而Runnable规定的方法是run(). 
Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。  
call()方法可抛出异常,而run()方法是不能抛出异常的只能内部消化。 
运行Callable任务可拿到一个Future对象, Future表示异步计算的结果。 
它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。 
通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。 
Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。
Callable和Future、FutureTask配合可以用来获取异步执行的结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。

线程池使用 FutureTask 时候需要注意的一点事,FutureTask 使用不当可能会造成调用线程一直阻塞,如何避免?

线程池使用 FutureTask 的时候如果拒绝策略设置为了 DiscardPolicy和DiscardOldestPolicy并且在被拒绝的任务的 Future 对象上调用无参 get 方法那么调用线程会一直被阻塞。

也就是说当 future 的状态 > COMPLETING 时候调用 get 方法才会返回,而明显 DiscardPolicy 策略在拒绝元素的时候并没有设置该 future 的状态,后面也没有其他机会可以设置该 future 的状态,所以 future 的状态一直是 NEW,所以一直不会返回,同理 DiscardOldestPolicy 策略也是这样的问题,最老的任务被淘汰时候没有设置被淘汰任务对于 future 的状态。

所以当使用 Future 的时候,尽量使用带超时时间的 get 方法,这样即使使用了 DiscardPolicy 拒绝策略也不至于一直等待,等待超时时间到了会自动返回的,如果非要使用不带参数的 get 方法则可以重写 DiscardPolicy 的拒绝策略在执行策略时候设置该 Future 的状态大于 COMPLETING 即可,但是查看 FutureTask 提供的方法发现只有 cancel 方法是 public 的并且可以设置 FutureTask 的状态大于 COMPLETING,重写拒绝策略具体代码可以如下:

public class MyRejectedExecutionHandler implements RejectedExecutionHandler {
public void rejectedExecution(Runnable runnable, ThreadPoolExecutor threadPoolExecutor) {
if (!threadPoolExecutor.isShutdown()) {
if(null != runnable && runnable instanceof FutureTask){
((FutureTask) runnable).cancel(true);
}
}
}
}

run():任务执行之后,设置s=COMPLETING=1原状态不是NEW就不设置,被cancel()改变了),在设置s=NORMAL=2正常完成)。任务执行异常之后,设置s=COMPLETING=1原状态不是NEW就不设置,被cacnel()改变了),再设置s=EXCEPTIONAL=3异常完成).

cancel(): false  NEW0->CANCELLED4     true: NEW0 ->INTERRUPTING5->中断线程->INTERRUPTED6 

会先设置s=INTERRUPTING=5或者s=CANCELLED=4原状态不是NEW就不设置,被run()改变了),设置成功之后,中断线程之后,再设置s=INTERRUPTED=6。(都表示异常了)。

cancel()方法的作用就是改变状态,中断异常,唤醒waiter。影响run()方法设置结果,影响get()awiatDone()获取结果抛出异常。

get():只有在s <= COMPLETING=1才去阻塞拿结果,s>1不阻塞直接去拿结果(结果是null,并且抛出异常)。

get()里面的awaitDone():等待结果时候,s > COMPLETING=1就返回(有可能取消或者异常了,得到的结果是null,并且抛出异常),s == COMPLETING=1就线程让步。

isCancelled():state >= CANCELLED=4

isDone():state != NEW=0

设置1:run()正常,run()异常

设置2:run()正常完成

设置3:run()异常完成

设置4:cancel(false)

设置5:cancel(true)第一步

设置6:cancel(true)中断后最后一步

public class BBD {
public static void main(String[] args) throws Exception { Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("正在下载中...");
return "Hello World!";
}
};
FutureTask1<String> futureTask = new FutureTask1<>(callable);// Callable变成FutureTask
Thread t1 = new Thread(futureTask,"任务正常执行线程");
t1.start();
// new Thread(futureTask).start();//run()方法里面第一个CAS就return了
// new Thread(futureTask).start();//run()方法里面第一个CAS就return了 for (int i = ; i < ; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
futureTask.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
} }, "get()线程" + (i+)).start(); new Thread(new Runnable() {
@Override
public void run() {
try {
futureTask.get(5L,TimeUnit.SECONDS);
} catch (Exception e) {
e.printStackTrace();
}
} }, "get(5L)线程" + (i+)).start();
} new Thread(new Runnable() {
@Override
public void run() {
futureTask.cancel(true);
} }, "cancel()线程").start(); boolean b = Thread.currentThread().isInterrupted();//cancel(true)会设置中断标记 //-------------------------------------------------------------------------------------------------- Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("正在下载中...");
}
}; FutureTask1<String> runnableTask = new FutureTask1<>(runnable, "我是返回的结果");
new Thread(runnableTask).start();
System.out.println("从网络下载的结果为:" + runnableTask.get());//Runnable没有返回值,这里返回期望值。
}
}

FutureTask源码的更多相关文章

  1. Java 多线程(五)—— 线程池基础 之 FutureTask源码解析

    FutureTask是一个支持取消行为的异步任务执行器.该类实现了Future接口的方法. 如: 取消任务执行 查询任务是否执行完成 获取任务执行结果(”get“任务必须得执行完成才能获取结果,否则会 ...

  2. FutureTask 源码分析

    FutureTask 源码分析,这个类的原理与我分析android当中的FutureTask类差不多[http://www.cnblogs.com/daxin/p/3802392.html] publ ...

  3. FutureTask 源码解析

    FutureTask 源码解析 版权声明:本文为本作者原创文章,转载请注明出处.感谢 码梦为生| 刘锟洋 的投稿 站在使用者的角度,future是一个经常在多线程环境下使用的Runnable,使用它的 ...

  4. Java多线程类FutureTask源码阅读以及浅析

    FutureTask是一个具体的实现类,实现了RunnableFuture接口,RunnableFuture分别继承了Runnable和Future接口,因此FutureTask类既可以被线程执行,又 ...

  5. FutureTask源码深度剖析

    FutureTask源码深度剖析 前言 在前面的文章自己动手写FutureTask当中我们已经仔细分析了FutureTask给我们提供的功能,并且深入分析了我们该如何实现它的功能,并且给出了使用Ree ...

  6. Java并发编程笔记之FutureTask源码分析

    FutureTask可用于异步获取执行结果或取消执行任务的场景.通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过Fu ...

  7. FutureTask源码解析

    在Java中一般通过继承Thread类或者实现Runnable接口这两种方式来创建多线程,但是这两种方式都有个缺陷,就是不能在执行完成后获取执行的结果,因此Java 1.5之后提供了Callable和 ...

  8. 并发编程—— FutureTask 源码分析

    1. 前言 当我们在 Java 中使用异步编程的时候,大部分时候,我们都会使用 Future,并且使用线程池的 submit 方法提交一个 Callable 对象.然后调用 Future 的 get ...

  9. Java Future 和 FutureTask 源码Demo

    Future 是一个接口,看源码有Future 和 FutreTask 使用Demo package java.util.concurrent; /** * A <tt>Future< ...

  10. FutureTask源码阅读

    FutureTask功能用法 类结构 源码中详细说明了FutureTask生命周期状态及变化 /** * The run state of this task, initially NEW. The ...

随机推荐

  1. C#使用FileSystemWatcher来监控指定文件夹,并使用TCP/IP协议通过Socket发送到另外指定文件夹

    项目需求: 局域网内有两台电脑,电脑A(Windows系统)主要是负责接收一些文件(远程桌面粘贴.FTP上传.文件夹共享等方式),希望能在A接收文件后自动传输到电脑B(Windows系统)来做一个备份 ...

  2. Gitlab的CI/CD初尝试

    初衷:今天公司的前端和测试人员吵起来了.原因是测试埋怨前端人员把Bug的状态更改为已解决,结果代码根本没提交,而前端人员埋怨测试测的太频繁了,需要打几个环境的包不方便.又要改东西又要频繁打包费时间.凡 ...

  3. 前端之:js

    JavaScript概述 ECMAScript和JavaScript的关系 1996年11月,JavaScript的创造者--Netscape公司,决定将JavaScript提交给国际标准化组织ECM ...

  4. head中的base标签:设置超链接的默认行为

    默认情况下,如果不指定超链接的target属性,则在当前窗口打开.使用head中的base可以制定超链接的base类,一切超链接都会继承它的属性. <html> <head> ...

  5. Vue笔记1

    index.html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> &l ...

  6. java的数据类型相关知识点

    总结就是八个字: 数据2型,四类八种 (个人理解,仅供参考) 解析图如下: 基本数据类型: 1.逻辑类:boolean 布尔类型,它比较特殊,布尔类型只允许存储true(真)或者false(假),不可 ...

  7. Redis入门学习(一):简介

    Redis是一个开源的.高性能的.基于键值对的缓存与存储系统,通过提供多种键值数据类型来适应不同场景下的缓存与存储需求.同时Redis的诸多高层级功能使其可以胜任消息队列.任务队列等不同的角色. 20 ...

  8. CTF必备技能丨Linux Pwn入门教程——ShellCode

    这是一套Linux Pwn入门教程系列,作者依据i春秋Pwn入门课程中的技术分类,并结合近几年赛事中出现的一些题目和文章整理出一份相对完整的Linux Pwn教程. 课程回顾>> Linu ...

  9. Arthas实践--抽丝剥茧排查线上应用日志打满问题

    现象 在应用的 service_stdout.log里一直输出下面的日志,直接把磁盘打满了: 23:07:34.441 [TAIRCLIENT-1-thread-1] DEBUG io.netty.c ...

  10. adb 连接夜神和逍遥模拟器

    然后运行cmd命令,cd到夜神安装目录,执行命令 adb connect 127.0.0.1:62001 nox_adb.exe connect 127.0.0.1:62001 逍遥模拟器 adb c ...