JDK8线程池-ThreadPoolExecutor执行结果FutureTask源码分析与优化

前言

    上一章讲述了线程池ThreadPoolExecutor的原理。在线程池的运算过程结束后可以有返回值

1. ExecutorService 示例

public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(6);

        List<Future<String>> list = new ArrayList();
        for (int i = 0; i < 6; i++) {
            Future<String> future = executorService.submit(()-> Thread.currentThread().getName());

            list.add(future);
        }

        list.stream().forEach((s)->{
            try {
                System.out.println(s.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });

        executorService.shutdown();
    }

Q:如何获取线程运算的返回值;如果中间某个线程阻塞,会有什么结果? 

2. 源码分析

2.1 submit方法分析

public abstract class AbstractExecutorService implements ExecutorService {  
   /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

    /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

    /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

其中RunnableFuture同时具有Runnable, Future的特性。

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

 可以看出,提交的线程时callable线程,因为runnable线程没有返回值

当然runnable线程也可以有计算结果,实际是包装成callable接口实现

2.2 分析核心方法newTaskFor

    /**
     * Returns a {@code RunnableFuture} for the given callable task.
     *
     * @param callable the callable task being wrapped
     * @param <T> the type of the callable's result
     * @return a {@code RunnableFuture} which, when run, will call the
     * underlying callable and which, as a {@code Future}, will yield
     * the callable's result as its result and provide for
     * cancellation of the underlying task
     * @since 1.6
     */
    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }

    /**
     * Returns a {@code RunnableFuture} for the given runnable and default
     * value.
     *
     * @param runnable the runnable task being wrapped
     * @param value the default value for the returned future
     * @param <T> the type of the given value
     * @return a {@code RunnableFuture} which, when run, will run the
     * underlying runnable and which, as a {@code Future}, will yield
     * the given value as its result and provide for cancellation of
     * the underlying task
     * @since 1.6
     */
    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }

引用FutureTask类,实现RunnableFuture类,其实就是一个Runnable接口。

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

    /** The underlying callable; nulled out after running */
    private Callable<V> callable;

    /**
     * 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
    }

    /**
     * Creates a {@code FutureTask} that will, upon running, execute the
     * given {@code Runnable}, and arrange that {@code get} will return the
     * given result on successful completion.
     *
     * @param runnable the runnable task
     * @param result the result to return on successful completion. If
     * you don't need a particular result, consider using
     * constructions of the form:
     * {@code Future<?> f = new FutureTask<Void>(runnable, null)}
     * @throws NullPointerException if the runnable is null
     */
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

传入了callable私有属性。

其中传入runnable接口需要计算结果的在此传入。Executors.callable(runnable, result)

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

    /**
     * A callable that runs given task and returns given result
     */
    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;
        }
    }

转换成了callable接口,直接返回我们传入的result结果。

这里要注意:定义了线程运行的状态

public class FutureTask<V> implements RunnableFuture<V> {
    /**
     * The run state of this task, initially NEW.  The run state
     * transitions to a terminal state only in methods set,
     * setException, and cancel.  During completion, state may take on
     * transient values of COMPLETING (while outcome is being set) or
     * INTERRUPTING (only while interrupting the runner to satisfy a
     * cancel(true)). Transitions from these intermediate to final
     * states use cheaper ordered/lazy writes because values are unique
     * and cannot be further modified.
     *
     * Possible state transitions:
     * NEW -> COMPLETING -> NORMAL
     * NEW -> COMPLETING -> EXCEPTIONAL
     * NEW -> CANCELLED
     * NEW -> INTERRUPTING -> INTERRUPTED
     */
    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;

其中callable属性就是我们的线程接口引用

outcome记录运行结果

runner和waiters适用于中间逻辑处理,使用了CAS原子特性

2.3 结果的由来

在我的上一篇分析线程池源码的文章https://blog.csdn.net/fenglllle/article/details/82790242

总结出了,线程池中线程的运作方式

通过factory创建一个新线程,在新线程中,run方法调用我们传入的runnable接口的run方法

那么在上面分析的FutureTask实现就是我们传入的runnable接口实例。

分析run方法

    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 {
                    //结果的获取就在这里,直接调用call方法
                    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);
        }
    }

直接调用result = c.call();获取结果

set(result);设置结果值。CAS操作

    protected void set(V v) {
        //更新状态,NEW为COMPLETING
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            //对结果进行赋值
            outcome = v;
            //最终状态,线程运行结束
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            //结束
            finishCompletion();
        }
    }

结束处理

/**
     * Removes and signals all waiting threads, invokes done(), and
     * nulls out callable.
     */
    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();

        //GC
        callable = null;        // to reduce footprint
    }

看完set结果的代码,我们看获取结果的源码。

2.4 获取结果get方法


    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        //判断状态
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

    //超时时间内获取线程结果
    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);
    }

awaitDone方法,及其重要方法,等待线程执行结束;中断线程;超时等

    /**
     * Awaits completion or aborts on interrupt or timeout.
     *
     * @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) {
                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)
                //阻塞结果的当前线程存入futrue对象的waiters
                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);
        }
    }

LockSupport.park(this);

阻塞获取结果,这是future的缺陷,前面的线程阻塞会阻塞后面执行的结果的获取。影响效率。

获取判断了线程运行状态,下面看report(int s)

    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);
    }

NORMAL状态才会有结果

分析了获取结果阻塞的原因,LockSupport.park(this); 由于结果的获取按线程的提交先后顺序,先提交的可能会阻塞后提交的线程的运行结果。

那么来看看JDK1.8的优化方案吧

3. ExecutorCompletionService

3.1 示例解析

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService e = Executors.newFixedThreadPool(6);
        ExecutorCompletionService executorService = new ExecutorCompletionService(e);


        for (int i = 0; i < 6; i++) {
            executorService.submit(() -> Thread.currentThread().getName());
        }

        for (int i = 0; i < 6; i++) {
            System.out.println(executorService.take().get());
        }

        e.shutdown();
    }

简洁很多,下面来看原理

3.2 源码解析

public class ExecutorCompletionService<V> implements CompletionService<V> {
    private final Executor executor;
    private final AbstractExecutorService aes;
    private final BlockingQueue<Future<V>> completionQueue;

看属性,多了一个BlockingQueue,或许这就是有价值的地方。

跟进

    /**
     * Creates an ExecutorCompletionService using the supplied
     * executor for base task execution and a
     * {@link LinkedBlockingQueue} as a completion queue.
     *
     * @param executor the executor to use
     * @throws NullPointerException if executor is {@code null}
     */
    public ExecutorCompletionService(Executor executor) {
        if (executor == null)
            throw new NullPointerException();
        this.executor = executor;
        this.aes = (executor instanceof AbstractExecutorService) ?
            (AbstractExecutorService) executor : null;
        this.completionQueue = new LinkedBlockingQueue<Future<V>>();
    }

设置了执行器,创建了一个 Integer.MAX_VALUE的单链表队列

    /**
     * Creates a {@code LinkedBlockingQueue} with a capacity of
     * {@link Integer#MAX_VALUE}.
     */
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

    /**
     * Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity.
     *
     * @param capacity the capacity of this queue
     * @throws IllegalArgumentException if {@code capacity} is not greater
     *         than zero
     */
    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
    }

单链表 

    /**
     * Linked list node class
     */
    static class Node<E> {
        E item;

        /**
         * One of:
         * - the real successor Node
         * - this Node, meaning the successor is head.next
         * - null, meaning there is no successor (this is the last node)
         */
        Node<E> next;

        Node(E x) { item = x; }
    }

看submit方法

    public Future<V> submit(Callable<V> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<V> f = newTaskFor(task);
        executor.execute(new QueueingFuture(f));
        return f;
    }

newTaskFor方法,跟进后发现跟 2.2 分析核心方法newTaskFor 没区别

    private RunnableFuture<V> newTaskFor(Callable<V> task) {
        if (aes == null)
            return new FutureTask<V>(task);
        else
            return aes.newTaskFor(task);
    }

new QueueingFuture(f),关键点,JDK1.8之前埋下的钩子

    /**
     * FutureTask extension to enqueue upon completion
     */
    private class QueueingFuture extends FutureTask<Void> {
        QueueingFuture(RunnableFuture<V> task) {
            super(task, null);
            this.task = task;
        }
        //重点来了,上面方法的钩子发挥作用
        protected void done() { completionQueue.add(task); }
        private final Future<V> task;
    }

 set方法

    protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }
    
    /**
     * Removes and signals all waiting threads, invokes done(), and
     * nulls out callable.
     */
    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
    }

    protected void done() { }//默认什么都不干

任务完成加入队列,就不存在阻塞的问题了。这样获取的结果的顺序就不是顺序的了。先执行的线程结果优先写入队列。

获取结果直接队列取值。

4. 总结

ExecutorCompletionService相比ExecutorService,引入链表阻塞队列,解决了线程运算结果获取阻塞的问题。效率提升。

问题依旧:队列长度Integer.MAX,注意内存溢出。

 

展开阅读全文

没有更多推荐了,返回首页