java Future线程管理详解

java | 2021-04-27 09:32:15

创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口。这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。
如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。
而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。

一.Future

Future是一个接口,它定义了5个方法:

  • boolean cancel(boolean mayInterruptIfRunning);
  • boolean isCancelled();
  • boolean isDone();
  • V get() throws InterruptedException, ExecutionException;

简单说明一下接口定义

  • boolean cancel(boolean mayInterruptInRunning) 取消一个任务,并返回取消结果。参数表示是否中断线程。
  • boolean isCancelled() 判断任务是否被取消
  • Boolean isDone()    判断当前任务是否执行完毕,包括正常执行完毕、执行异常或者任务取消。
  • V get() 获取任务执行结果,任务结束之前会阻塞。
  • V get(long timeout, TimeUnit unit) 在指定时间内尝试获取执行结果。若超时则抛出超时异常

 

demo:

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class Demo {



    /**
     * Callalbe和Runnable的区别
     * 
     * Runnable run方法是被线程调用的,在run方法是异步执行的
     * 
     * Callable的call方法,不是异步执行的,是由Future的run方法调用的
     * 
     * 
     * 
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {

        Callable<Integer> call = new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                System.out.println("正在计算结果...");
                Thread.sleep(3000);
                return 1;
            }
        };

        FutureTask<Integer> task = new FutureTask<>(call);

        Thread thread = new Thread(task);
        thread.start();

        // do something
        System.out.println(" 干点别的...");

        Integer result = task.get();

        System.out.println("拿到的结果为:" + result);

    }

}

 

二.FutureTask实现原理
下面我们介绍一下FutureTask内部的一些实现机制

1 类继承结构

首先我们看一下FutureTask的继承结构:

FutureTask实现了RunnableFuture接口,而RunnableFuture继承了Runnable和Future,也就是说FutureTask既是Runnable,也是Future。

 

三.核心成员变量
FutureTask内部定义了以下变量,以及它们的含义如下

volatile int state:表示对象状态,volatile关键字保证了内存可见性。futureTask中定义了7种状态,代表了7种不同的执行状态

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; //任务线程已中断
  • Callable callable:被提交的任务
  • Object outcome:任务执行结果或者任务异常
  • volatile Thread runner:执行任务的线程
  • volatile WaitNode waiters:等待节点,关联等待线程
  • long stateOffset:state字段的内存偏移量
  • long runnerOffset:runner字段的内存偏移量
  • long waitersOffset:waiters字段的内存偏移量

后三个字段是配合Unsafe类做CAS操作使用的。

FutureTask对象初始化时,在构造器中把state置为为NEW,之后状态的变更依据具体执行情况来定。

 例如任务执行正常结束前,state会被设置成COMPLETING,代表任务即将完成,接下来很快就会被设置为NARMAL或者EXCEPTIONAL,这取决于调用Runnable中的call()方法是否抛出了异常。有异常则后者,反之前者。

  任务提交后、任务结束前取消任务,那么有可能变为CANCELLED或者INTERRUPTED。在调用cancel方法时,如果传入false表示不中断线程,state会被置为CANCELLED,反之state先被变为INTERRUPTING,后变为INTERRUPTED。

总结下,FutureTask的状态流转过程,可以出现以下四种情况:
1. 任务正常执行并返回。 NEW -> COMPLETING -> NORMAL
2. 执行中出现异常。NEW -> COMPLETING -> EXCEPTIONAL
3. 任务执行过程中被取消,并且不响应中断。NEW -> CANCELLED
4.任务执行过程中被取消,并且响应中断。 NEW -> INTERRUPTING -> INTERRUPTED 

登录后即可回复 登录 | 注册
    
关注编程学问公众号