【J.U.C-executors】执行器框架——Future模式

Future模式

  1. 在Java中如果要执行一个任务,一般要实现Runnable接口:
public class MyTask implements Runnable {
    @Override
    public void run() {
       
    }
}
  1. 但是如果需要得到任务执行的结果,则需要继承Callable接口:
public class MyTask implements Callable {
    @Override
    public Object call() {

    }
}
  • 背景:一般我们调用一个函数,只有等待函数执行结束后,才能继续往下执行;但是如果函数的执行时间较长,那么我们会使用异步任务的形式,创建一个新的线程进行调用。但是这种方式我们将不能得到其执行结果。

Future模式用于 异步 地获取任务的 执行结果

  • Future模式让调用方调用任务执行之后拿到一个 凭证(Future) ,然后立即返回;
  • 调用者可以先去执行其他的任务;
  • 在需要的时候,根据凭证去获取任务的执行结果。

Future接口、FutureTask类

Future即为我们获取到的凭证,它是一个接口:

public interface Future<V> {
 
    boolean cancel(boolean mayInterruptIfRunning);
 
    boolean isCancelled();
 
    boolean isDone();
 
    V get() throws InterruptedException, ExecutionException;
 
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}
  • isCancelled:监控任务是否被取消
  • isDone:监控任是否执行结束
  • cancel:取消任务的执行
  • get:用于获取任务执行结果,如果调用时还没执行结束,将会阻塞(除非设置超时时间)

J.U.C为我们提供了Future接口的实现类:FutureTask。

使用Demo

创建任务,demo中是Runnable类型任务无返回值,也可以创建Callable带返回值的任务:

class MyRunnableTask implements Runnable {

    @Override
    public void run() {
        try {
            //任务执行耗时
            System.out.println("任务开始执行");
            Thread.sleep(3000);
            System.out.println("任务执行结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

有两种形式使用Future的方式:

  1. 直接使用Thread与FutureTask:
public class TestFuture {

    public static void main(String[] args) {
        MyRunnableTask myRunnableTask = new MyRunnableTask();
        FutureTask future = new FutureTask(myRunnableTask, null);
        Thread thread = new Thread(future);
        thread.start();
        System.out.println("main线程继续执行");
        try {
            future.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("main线程执行结束");
    }
}
  1. 根据ExecutorService的submit方法:
public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        MyRunnableTask myRunnableTask = new MyRunnableTask();
        Future future = executorService.submit(myRunnableTask);
        executorService.shutdown();
        try {
            future.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
}

看submit方法内部,其实也是创建了一个FutureTask进行返回:

	public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

	protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }

Future接口实现类——FutureTask

1. 类继承关系

在这里插入图片描述
可知FutureTask是Future接口的实现类,另外还继承了Runnable接口,所以其本质是一个异步的任务

  • 一方面是其Task的性质:我们可以将其提交给ThreadPoolExecutor执行,也可以直接调用run方法执行;
  • 另一方面是其Future的性质:我们可以对这个任务进行额外的操作,监控是否执行结束、获取执行结果等。

2. 存储结构与构造函数

 	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;

    /** The underlying callable; nulled out after running */
    private Callable<V> callable;
    /** The result to return or exception to throw from get() */
    private Object outcome; // non-volatile, protected by state reads/writes
    /** The thread running the callable; CASed during run() */
    private volatile Thread runner;
    /** Treiber stack of waiting threads */
    private volatile WaitNode waiters;

从JDK1.8开始,FutureTask使用volatile+CAS来实现同步,之前是实现了AQS框架。

  • state:任务状态值,volatile线程可见
  • callable:Callable类型的任务
  • outcome:调用get方法得到的返回值
  • runner:执行callable任务的Thread线程
  • waiters:一个链表结构,存储等待的线程
    WaitNode这个单向链表,在FutureTask中被当做线程安全的栈来使用,针对于多个线程同时入栈的情况(多个线程同时调用get方法)使用CAS保证入栈的线程安全,出栈也同理。
static final class WaitNode {
        volatile Thread thread;
        volatile WaitNode next;
        WaitNode() { thread = Thread.currentThread(); }
    }

此外还有一系列的UnSafe值记录:

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

构造函数:

public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }
  • 可见无论是Runnable类型还是Callable类型的任务都可以创建,它们最终都会被封装为Callable;
  • 任务的初始状态设为NEW

任务状态

  • NEW:表示任务的初始化状态;
  • COMPLETING:表示任务已执行完成(正常完成或异常完成),但任务结果或异常原因还未设置完成,属于中间状态;
  • NORMAL:表示任务已经执行完成(正常完成),且任务结果已设置完成,属于最终状态;
  • EXCEPTIONAL:表示任务已经执行完成(异常完成),且任务异常已设置完成,属于最终状态;
  • CANCELLED:表示任务还没开始执行就被取消(非中断方式),属于最终状态;
  • INTERRUPTING:表示任务还没开始执行就被取消(中断方式),正式被中断前的过渡状态,属于中间状态;
  • INTERRUPTED:表示任务还没开始执行就被取消(中断方式),且已被中断,属于最终状态。

可能的状态流转:

  1. NEW -> COMPLETING -> NORMAL
  2. NEW -> COMPLETING -> EXCEPTIONAL
  3. NEW -> CANCELLED
  4. NEW -> INTERRUPTING -> INTERRUPTED
  • 注意COMPLETING是已执行结束的状态,只是还未设置返回的结果。
  • 也就是说只要不是NEW状态,任务都已执行完了

3.结果获取

先看get方法:

public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }
  1. 如果任务的状态<COMPLETING,代表任务还未执行结束,调用awaitDone
  2. 调用report返回结果

3.1 awaitDone阻塞线程,返回任务状态

任务未执行完,将调用get的线程加入阻塞队列,挂起线程

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) {
                if (q != null)
                    q.thread = null;
                return s;
            }
            else if (s == COMPLETING) // cannot time out yet
                Thread.yield();
            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);
        }
    }

首先区分两种线程的概念:

  • 一类是执行任务的线程(只有一个)执行FutureTask的run方法;
  • 一类是获取任务执行结果的线程(可以有多个,可并发执行)调用get方法来获取任务的执行结果。
    如果任务还没有执行完,则这些线程就需要进入线程安全的栈(也就是等待队列)中挂起,直到任务执行结束,或者等待的线程自身被中断。

再看其流程, 整体框架是一个自旋操作;

  1. 先检查当前线程是否被中断,如果被中断,removeWaiter将node从等待队列中移除
  2. 检查任务状态,如果这时已经进入终止状态,返回其状态;
  3. 如果处于COMPLETING执行完毕,还未设置结果完成的状态,Thread.yield(),让出当前线程的cpu继续等待;
  4. 否则任务正在执行中,创建一个节点q,并加入到链表的头结点之前,即入栈操作;
  5. 最后LockSupport.park将线程挂起。

自旋操作一直到线程被中断,或者是此线程再任务执行结束之后,finishCompletion方法中被唤醒。

3.2 report根据任务状态返回结果

private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }
  • 如果状态为正常,返回结果;
  • 状态为被取消、或有异常,返回相应的异常;

4.任务执行

public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            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);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
  • 通过CAS将runner设置为当前线程,记录执行任务的线程;
  • 调用call方法执行任务;
  • 执行任务成功,set(result)设置结果;执行失败,setException(ex)返回异常;
  1. 执行成功:set(result)
	protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }
  • 将stats由NEW改为COMPLETING
  • 给outcome赋值;
  • 将state修改为NORMAL最终态
  • finishCompletion完成执行结果的设置
  1. 执行失败:setException(ex)
	protected void setException(Throwable t) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = t;
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            finishCompletion();
        }
    }
  • 将stats由NEW改为COMPLETING
  • 给outcome赋值为Throwable异常;
  • 将state修改为EXCEPTIONAL最终态
  • finishCompletion完成执行结果的设置

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
    }
  • 将waiters的值设为null,目的就是清空整个栈;
  • 在for循环中遍历整个单向链表,LockSupport.unpark唤起等待线程;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值