FutureTask是一种可以取消的异步的计算任务。它的计算是通过Callable实现的,多用于耗时的计算。

一.FutureTask的三种状态

二.get()和cancel()执行示意

三.使用

一般FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。

3.1 FutureTask + Thread

package concurrent;  

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask; /**
*
* @author Administrator
*
*/
@SuppressWarnings("all")
public class FutureTaskDemo {
public static void main(String[] args) {
//step2:创建计算任务,作为参数,传入FutureTask
CalculateTask cTask = new CalculateTask();
FutureTask futureTask = new FutureTask(cTask);
//step3:将FutureTask提交给Thread执行
Thread pAccountThread = new Thread(futureTask);
System.out.println("futureTask线程现在开始启动,启动时间为:" + System.nanoTime());
pAccountThread.start();
System.out.println("主线程开始执行其他任务");
// 从其他账户获取总金额
int totalMoney = new Random().nextInt(100000);
System.out.println("现在你在其他账户中的总金额为" + totalMoney);
System.out.println("等待私有账户总金额统计完毕...");
// step4:测试后台的计算线程是否完成,如果未完成则等待
while (!futureTask.isDone()) {
try {
Thread.sleep(500);
System.out.println("私有账户计算未完成继续等待...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("futureTask线程计算完毕,此时时间为" + System.nanoTime());
Integer privateAccountMoney = null;
//step5:获取执行结果
try {
privateAccountMoney = (Integer) futureTask.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("您现在的总金额为:" + totalMoney + privateAccountMoney.intValue());
}
} /**
* @author admin
*step1:封装一个计算任务,计算私有账户余额,实现Callable接口
*/
@SuppressWarnings("all")
class CalculateTask implements Callable {
Integer totalMoney; @Override
public Object call() throws Exception {
//模拟耗时操作
Thread.sleep(5000);
totalMoney = new Integer(new Random().nextInt(10000));
System.out.println("您当前有" + totalMoney + "在您的私有账户中");
return totalMoney;
}
}

执行结果:

futureTask线程现在开始启动,启动时间为:57541141963086
主线程开始执行其他任务
现在你在其他账户中的总金额为30431
等待私有账户总金额统计完毕...
私有账户计算未完成继续等待...
私有账户计算未完成继续等待...
私有账户计算未完成继续等待...
私有账户计算未完成继续等待...
私有账户计算未完成继续等待...
私有账户计算未完成继续等待...
私有账户计算未完成继续等待...
私有账户计算未完成继续等待...
私有账户计算未完成继续等待...
您当前有4831在您的私有账户中
私有账户计算未完成继续等待...
futureTask线程计算完毕,此时时间为57546161371100
您现在的总金额为:304314831

3.2 Future + ExecutorService

//step1 ......
//step2:创建计算任务
Task task = new Task();
//step3:创建线程池,将Callable类型的task提交给线程池执行,通过Future获取子任务的执行结果
ExecutorService executorService = Executors.newCachedThreadPool();
final Future<Boolean> future = executorService.submit(task);
//step4:通过future获取执行结果
boolean result = (boolean) future.get();

四.源码解析

4.1 状态

private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;

4.2 构造函数

/**
* Creates a {@code FutureTask} that will, upon running, execute the
* given {@code Callable}.
*
* @param callable the callable task
* @throws NullPointerException if the callable is null
*/
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}

可以看到FutrueTask传入的是一个Callable类型的变量,将传入的参数赋值给this.callable,然后设置state状态为NEW,表示这是新任务。

也可以使用Runnable+result组合成一个Callable

public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}

4.3执行任务——run()方法

public void run() {
//1.判断状态是否是NEW,不是NEW,说明任务已经被其他线程执行,甚至执行结束,或者被取消了,直接返回
//2.调用CAS方法,判断runnerOffset为null的话,就将当前线程保存到runnerOffset中
//设置runnerOffset失败,就直接返回
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
//执行callable任务,返回result
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
//遇到异常,设置异常
setException(ex);
}
//执行完毕,设置result
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
//设置runner为null
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
//如果被中断了,处理中断
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}

看一下runnerOffset是啥:

private static final sun.misc.Unsafe UNSAFE;private static final long runnerOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> k = FutureTask.class;
runnerOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("runner"));
} catch (Exception e) {
throw new Error(e);
}
}

可以看到它指向runner字段的偏移地址,相当于指针

发生异常:

protected void setException(Throwable t) {
     // stateOffset NEW --> COMPLETING
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
//返回异常信息
outcome = t;
//stateOffset --> EXCEPTIONAL
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
//结束
finishCompletion();
}
}

正常执行:

protected void set(V v) {
//state New --> COMPLETING
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
//返回结果
outcome = v;
//state --> NORMAL
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
//结束
finishCompletion();
}
}

被中断:

private void handlePossibleCancellationInterrupt(int s) {
// It is possible for our interrupter to stall before getting a
// chance to interrupt us. Let's spin-wait patiently.
if (s == INTERRUPTING)
while (state == INTERRUPTING)
//让出线程
Thread.yield();
}

4.4 获取结果——get()方法

public V get() throws InterruptedException, ExecutionException {
//获取状态
int s = state;
//如果没有完成则等待
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}

进去看一下awaitDone()方法

private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false; for (;;) {
//如果被中断,则移除wait节点,并返回异常
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
//1 得到状态
int s = state;
//1.1 如果s > COMPLETING,直接返回
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
//1.2 如果s == COMPLETING但是还没写到outcome,表示任务结束,让出线程
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
//q == null,则 new 一个新的节点
else if (q == null)
q = new WaitNode();
//如果还没有加入等待队列,则加入队列头
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
//如果设置了超时时间
else if (timed) {
nanos = deadline - System.nanoTime();
//时间到了移除节点,停止等待
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
//时间没到,阻塞,等待
LockSupport.parkNanos(this, nanos);
}
//没设置则阻塞,等待
else
LockSupport.park(this);
}
}

4.5 cancel()方法

public boolean cancel(boolean mayInterruptIfRunning) {
//state != NEW,返回false
//state == NEW,判断是否要中断任务的执行,是则stateOffset=INTERRUPTING,否则stateOffset=CANCELLED
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
//读取当前正在执行子任务的线程runner,调用t.interrupt(),中断线程执行
if (t != null)
t.interrupt();
} finally { // final state
//stateOffset = INTERRUPTED
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}

4.6 最终方法finishCompletion(),释放资源

private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
} done(); callable = null; // to reduce footprint
}

Java并发编程的艺术笔记(九)——FutureTask详解的更多相关文章

  1. Java并发编程:线程封闭和ThreadLocal详解

    转载请标明出处: http://blog.csdn.net/forezp/article/details/77620769 本文出自方志朋的博客 什么是线程封闭 当访问共享变量时,往往需要加锁来保证数 ...

  2. 多线程的通信和同步(Java并发编程的艺术--笔记)

    1. 线程间的通信机制 线程之间通信机制有两种: 共享内存.消息传递.   2. Java并发 Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式执行,通信的过程对于程序员来说是完全透 ...

  3. Java并发编程的艺术笔记(二)——wait/notify机制

    一.概述 一个线程修改了一个对象的值,另一个线程感知到变化从而做出相应的操作.前者是生产者,后者是消费者. 等待/通知机制,是指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用 ...

  4. Java并发编程的艺术笔记(七)——CountDownLatch、CyclicBarrier详解

    一.等待多线程完成的CountDownLatch CountDownLatch允许一个或多个线程等待其他线程完成操作,像加强版的join.(t.join()是等待t线程完成) 例: (1)开启多个线程 ...

  5. Java并发编程的艺术笔记(五)——Java中的锁

    一.Lock接口的几个功能: 显示的获取和释放锁 尝试非阻塞的获取锁 能被中断的获取锁 超时获取锁 使用方式: Lock lock = new ReentrantLock(); lock.lock() ...

  6. Java并发编程的艺术· 笔记(1)

    目录 1.volatile的原理 2.Synchonized 3.无锁-偏向锁-轻量级锁-重量级锁 4.Java实现原子操作 1.volatile的原理 如何保持可见性: 1)将当前处理器缓存行的数据 ...

  7. Java并发编程的艺术笔记(八)——线程池

    一.线程池的主要处理流程 ThreadPoolExecutor执行execute方法分下面4种情况. 1)如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步需要获 ...

  8. Java并发编程的艺术笔记(三)——Thread.join()

    t.join()方法只会使主线程进入等待池并等待t线程执行完毕后才会被唤醒.并不影响同一时刻处在运行状态的其他线程.它能够使得t.join()中的t优先执行,当t执行完后才会执行其他线程.能够使得线程 ...

  9. Java并发编程的艺术笔记(一)——volatile和syncronized关键字

    一.线程间的通信 volatile和syncronized关键字 volatile 修饰变量,告知任何对该变量的访问必须从共享内存获取,对它的改变必须同步刷新至共享内存,由此保证可见性. syncro ...

随机推荐

  1. MongoDB v4.0 命令

    MongoDB v4.0 命令 官方文档 > 点这里 < 操作系统库 #操作管理员库 use admin #鉴权 db.auth("root","admin& ...

  2. springboot(十七)-使用Docker部署springboot项目

    Docker 技术发展为微服务落地提供了更加便利的环境,使用 Docker 部署 Spring Boot 其实非常简单,这篇文章我们就来简单学习下. 首先构建一个简单的 Spring Boot 项目, ...

  3. python 练习合集一

    一.运算符与流程控制 1.输入两个整数,打印较大的那个值2.输入三个整数,按照从小到大的顺序打印3.输入一个三位数,打印其个位.十位.百位上的数4.输入一个年份,判断是否为闰年,是打印一句话,不是打印 ...

  4. openlayers之全屏控件的使用

    import { FullScreen } from 'ol/control' map.addControl(new FullScreen())

  5. 一头扎进 JAVA

    硅不可 吉米 JAVA 基础 -- 基础不牢,地动山摇 子类应该比 父类更为 开放 (public protected default private) 子类方法不能比父类抛出更高异常( 可以为父类方 ...

  6. Oracle VM VirtualBox 安装 Centos7 并配置静态IP

    新建虚拟电脑 选择类型和版本 设置内存大小 现在创建虚拟硬盘 选择VDI 硬盘足够就选择 固定大小 选择虚拟硬盘存储位置不要放在默认的C盘下,设置大小为30G 等待创建虚拟硬盘 虚拟硬盘创建成果之后 ...

  7. 查看Linux服务器硬件信息

    一:查看cpu# 总核数 = 物理CPU个数 X 每颗物理CPU的核数 # 总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数# 查看物理CPU个数cat /proc/cpui ...

  8. js中 base64 转二进制

    function base64toBlob(base64,type) { // 将base64转为Unicode规则编码 let bstr = atob(base64, type), n = bstr ...

  9. iQOO5G手机卡槽公布

    华为.中兴.一加.OPPO.vivo各自的5G手机相继拿到了3C强制认证,小米的也在路上,5G手机真的越来越近了. 今天,vivo iQOO还公布了其5G手机所用的卡槽样式,可以看到采用了双卡设计,同 ...

  10. NB-IOT双工模式

    半双工(Half Duplex)数据传输指数据可以在一个信号载体的两个方向上传输,但是不能同时传输.例如,在一个局域网上使用具有半双工传输的技术,一个工作站可以在线上发送数据,然后立即在线上接收数据, ...