线程:Future、ExecutorService源码解析


Runnable创建的是没有返回值的线程,同时还可以创建有返回值的线程。

1.整体架构

线程API之间的关系如下图,
image
通过下面的代码对各个API的使用做演示,

// 创建一个线程池(实际开发时线程池不是这样创建的,这里只是演示)
ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 3, 0L, TimeUnit.MILLISECONDS,
                                                     new LinkedBlockingQueue<>());
// futureTask构造器的入参是 Callable
FutureTask futureTask = new FutureTask(new Callable<String> () {
  @Override
  public String call() throws Exception {
    Thread.sleep(3000);
    return "我是子线程"+Thread.currentThread().getName();
  }
});
// 把任务提交到线程池中,线程池会分配线程执行任务
executor.submit(futureTask);
// 得到任务执行的结果
String result = (String) futureTask.get();
log.info("result is {}",result);
  • Callable:定义需要执行的任务,可以有返回值
  • FutureTask:线程任务,入参是Callable,是对Callable的包装,方便线程池的使用。通过FutureTask.get方法获取子线程计算结果

2.Callable接口

Callable接口定义了线程执行任务的代码,与Runnable的作用相同,区别是Callable有返回值。接口的定义如下,

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

返回值是泛型,使用时不会直接使用Callable,而是会结合FutureTask一起使用。

3.FutureTask类

FutureTask是线程的具体执行,该类实现了RunnableFuture接口。RunnableFuture接口是Runnable和Future接口的子接口。通过这一个继承关系逐一进行说明。

Future接口

Callable接口可以返回子线程执行结果,而Future接口用于获取Callable接口实现类的返回结果

Future接口的类注释,

  1. Future定义了异步计算。接口的方法用于检查计算是否完成,等待计算完成,并取回计算结果等方法
  2. 使用 get方法可以得到结果,该方法会一直阻塞到子线程任务完成才返回
  3. cancel 方法可以取消任务
// 如果任务执行成功或已被取消,则无法再取消的,直接返回true
// 如果任务还没被进行时,发起取消,可以取消成功的。
// 如果取消时,任务已经在运行了,mayInterruptIfRunning 为 true 的话,就可以打断运行中的线程;反之,表示不能打断直接返回
boolean cancel(boolean mayInterruptIfRunning);

// 返回线程是否已经被取消了,true 表示已经被取消了
// 如果线程已经运行结束了,isCancelled 和 isDone 返回的都是 true
boolean isCancelled();

// 线程是否已经运行结束了
boolean isDone();

// 等待结果返回
// 如果任务被取消了,抛 CancellationException 异常
// 如果等待过程中被打断了,抛 InterruptedException 异常
V get() throws InterruptedException, ExecutionException;

// 等待,但是带有超时时间的,如果超时时间外仍然没有响应,抛 TimeoutException 异常
V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;

Future接口定义了各种对任务进行管理的方法。

RunnableFuture接口

RunnableFuture也是一个接口,

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

RunnableFuture接口的目的是将Runnable和Future进行整合,让Future对Runnable进行管理

统一Callable和Runnable

之前对任务的创建方式进行了拓展,

  • 无返回值的 Runnable
  • 有返回值的 Callable

两种接口的定义使得任务的创建方式并不统一,所以在 FutureTask类实现了RunnableFuture接口,同时集合了Callable接口(因为 Callable是FutureTask的属性),还提供了两者的转化方法

1)FutureTask类定义

public class FutureTask<V> implements RunnableFuture<V> {}

从类定义上可以看出来 FutureTask 实现了 RunnableFuture 接口,即间接实现了 Runnnable 接口,故FutureTask 本身就是个 Runnnable。同时 FutureTask 也实现了 Future,也就是说 FutureTask 具备对任务进行管理的功能。

2)FutureTask类属性

// 任务状态
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;//任务被打断成功

// 组合了 Callable 
private Callable<V> callable;
// 异步线程返回的结果
private Object outcome; 
// 当前任务所运行的线程
private volatile Thread runner;
// 记录调用 get 方法时被等待的线程
private volatile WaitNode waiters;

Callable是FutureTask的属性,这使得 FutureTask具备了将Runnable转化为Callable的前提。

3)FutureTask构造器

FutureTask类有两个构造器,分别接收 Callable和Runnable作为参数,

// 使用 Callable 进行初始化
public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    // 任务状态初始化
    this.state = NEW;       // ensure visibility of callable
}

// 使用 Runnable 初始化,并传入result 作为返回结果。
// Runnable 是没有返回值的,所以 result 一般设置为null
public FutureTask(Runnable runnable, V result) {
    // Executors.callable 方法把Runnable适配成RunnableAdapter,RunnableAdapter实现了Callable
    // 所以也就是把 runnable 直接适配成了 callable。
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}

FutureTask的两个构造器最终的目的是将入参都转化为Callable。

4)Runnable适配Callable

当FutureTask的构造方法传入的参数为Runnable类型,会使用Executors类的callable方法将 Runnable转化为 Callable。因为Runnable和Callable都是接口,无法直接转化,所以此处引入一个RunnableAdapter类进行转化。该类是Executors类的静态内部类,相当于一个适配器,将Runnable适配为Callable。

Executors类是一个工厂类,callable方法创建一个 RunnableAdapter对象并返回,

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

RunnableAdapter类中对 Runnable的适配方式很简单,

static final class RunnableAdapter<T> implements Callable<T> {
    final Runnable task;
    final T result;
    RunnableAdapter(Runnable task, T result) {
        this.task = task;
        this.result = result;
    }
    public T call() {
        task.run();
        return result;
    }
}
  1. RunnableAdapter实现了Callable接口,所以该类本身就是任务类
  2. Runnable是RunnableAdapter的一个属性,在RunnableAdapter复写的 Callable的call方法中,调用了Runnable的run方法。

FutureTask对任务的管理

Future接口对任务的管理定义了一系列方法,FutureTask是Future接口的子类,所以FutureTask对任务管理方法进行了复写。几个较为关键的方法进行说明,

1)get方法

get方法有无限阻塞和设置超时时间两种,一般使用设置超时时间的方法,

public V get(long timeout, TimeUnit unit)
    throws InterruptedException, ExecutionException, TimeoutException {
    if (unit == null)
        throw new NullPointerException();
    int s = state;
    if (s <= COMPLETING &&
        (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
        throw new TimeoutException();
    return report(s);
}

对几处代码进行说明,

 // 任务正在被执行,且等待一定的时间后,仍然在执行中,直接抛出异常
(s <= COMPLETING && (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)

awaitDone方法源码如下,

/**
 * 返回线程任务执行结果,完成、中断或超时
 * @param timed true if use timed waits
 * @param nanos time to wait, if timed
 * @return state upon completion
 */
private int awaitDone(boolean timed, long nanos)
    throws InterruptedException {
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    WaitNode q = null;
    boolean queued = false;				// 不排队
    for (;;) {
        if (Thread.interrupted()) {		// 如果线程已被打断,删除抛出异常
            removeWaiter(q);
            throw new InterruptedException();
        }

        int s = state;
        if (s > COMPLETING) {		  // 任务已经执行结束(完成、取消或打断),返回任务状态s
            if (q != null)			  // q是WaitNode对象(FutureTask的内部类),用于记录等待线程
                q.thread = null;	  // WaitNode的thread属性指向当前线程,线程执行完成后将该线程置空
            return s;
        }
        else if (s == COMPLETING)	// 如果任务正在执行且未超时,当前线程让出cpu使用权,重新竞争
            Thread.yield();
        else if (q == null)
            q = new WaitNode();		// 第一次运行时新建 WaitNode,当前线程就是WaitNode对象的thread属性
            
        else if (!queued)			// 第一次都会执行这里,执行成功之后,queued为true,不会再执行。新建的WaitNode对象当做waiters链表的第一个元素
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset,	
                                                 q.next = waiters, q);
       
        // 任务既没有被运行也没有结束,对应NEW状态                                           
        else if (timed) {			// 如果设置超时
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {		// 已经超时,从链表中删除当前WaitNode节点
                removeWaiter(q);
                return state;
            }
            LockSupport.parkNanos(this, nanos);	// 还未超时则进入TIMED_WAITING状态
        }
        else					 // 未设置超时,进入WAITING状态
            LockSupport.park(this);
    }
}

get 方法中做了很多 wait 的事情,当发现任务还在进行中,没有完成时,就会阻塞当前进程,等待任务完成后再返回结果值。阻塞底层使用的是 LockSupport.park 方法,使当前线程进入 WAITING 或 TIMED_WAITING 状态。

2)run方法

public void run() {
    // 任务不是新建的或者当前任务正在被执行,直接返回
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        // Callable不为空,该任务创建成功且未被执行,状态为NEW
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();	 // 执行任务
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                set(result);	// 给返回值
        }
    } finally {
        runner = null;
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

在执行 c.call() 代码时,

  • 如果入参是 Runnable 的话, 调用路径为 c.call() -> RunnableAdapter.call() -> Runnable.run()
  • 如果入参是 Callable 的话,直接调用Callable的call方法

3)cancel方法

public boolean cancel(boolean mayInterruptIfRunning) {
    if (!(state == NEW &&	//任务状态不是创建 并且不能把 new 状态置为取消,直接返回 false
          UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
              mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        return false;
        
    // 进行取消操作,打断可能会抛出异常
    try { 
        if (mayInterruptIfRunning) {	// mayInterruptIfRunning 为 true 的话,就可以打断运行中的线程
            try {
                Thread t = runner;
                if (t != null)
                    t.interrupt();
            } finally {
                //状态设置成已打断
                UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
            }
        }
    } finally {
        // 清理线程
        finishCompletion();
    }
    return true;
}

总结

FutureTask是关键,通过 FutureTask 把 Runnnable、Callable、Future 都串起来了,使 FutureTask 具有三者的功能,统一了 Runnnable 和 Callable。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章