FutureTask 可获取结果的异步请求

1. FutureTask是什么?

FutureTask是一个可取消的异步计算。FutureTask实现了RunnableFuture接口,间接实现了Future和Runnable接口。在Future的基本实现中,提供了开始和取消一个计算的方法,同时可以查询计算是否完成并获取到计算结果。 仅当完成计算之后才能获取到计算结果,否则get方法将阻塞,直到计算完成。 计算完成之后这个计算将不能够重启或者取消,除非调用RunAndReset方法。
Runnable的实现,赋予了FutureTask包裹Runnable或Callable的能力,因此FutureTask可以被提交到Executor中执行。

interface RunnableFuture extends Runnable, Future{}
class FutureTask implements RunnableFuture{}

FutureTask的状态转换

FutureTask任务的运行状态,最初为NEW。运行状态仅在set、setException和cancel方法中转换为终端状态。在完成过程中,状态可能呈现出瞬时值INTERRUPTING(仅在中断运行程序以满足cancel(true)的情况下)或者COMPLETING(在设置结果时)状态时。从这些中间状态到最终状态使用 cheaper ordered/lazy writes(无锁同步CAS)策略, 原因是这些状态值是固定不可修改的。

  • NEW -> COMPLETING -> NORMAL
  • NEW -> COMPLETING -> EXCEPTIONAL
  • NEW -> CANCELLED
  • NEW -> INTERRUPTING -> INTERRUPTED

2. FutureTask实现

这个类在实现上jdk 1.8 版本前后版本实现方式有些不同,之前使用的策略是AbstractQueuedsynchronizer也就是和ReentrantLock、ThreadPoolExecutor中的worker策略相同。 1.8版本是使用Usafe包中的Compare-And-Swap(CAS)特性实现。

Usafe包?
sun.misc.Unsafe 是sun公司的一个未开源的组件包,提供了些可以绕开JVM的一些方法,其中就包含了同步相关的一些方法。比如monitorEnter(),tryMonitorEnter(),monitorExit(),compareAndSwapInt(),putOrderedInt()。

什么是CAS?
CAS是CPU提供的 用于解决多线程并行情况下使用锁造成性能损耗 的机制。

如此优秀的特性,是不是尽情用就好了,当然不会尽善尽美,首先在Unsafe包中所有操作绕开了JVM意味着不会有人替你管理内存。 而且CAS会引入ABA问题。 更多内容请参考资料[1]

毋庸置疑根据前面描述的特性,FutureTask至少有一个cancel(), V get() 的public方法。

public boolean cancel(boolean mayInterruptIfRunning) {
        if (!(state == NEW &&
              U.compareAndSwapInt(this, STATE, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {    // in case call to interrupt throws exception
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // final state
                    U.putOrderedInt(this, STATE, INTERRUPTED);
                }
            }
        } finally {
            finishCompletion();
        }
        return true;
    }

 /**
     * @throws CancellationException {@inheritDoc}
     */
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }


cance()方法很好理解,就做量两件事1. 还能不能取消了,要不要终止线程取消。 2. 设置对应的状态。
get()方法是去获取执行结果,但是现在如果没执行完呢? 前面说如果没执行完会阻塞等待,显然这是s = awaitDone(false, 0L);干的事情。

好像也不复杂。 对喽,稍微复杂的点地方在怎么去处理这个等待,怎么正确的同步处理状态。

/**
     * 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 or at timeout
     */
    private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        // 下面的代码巧妙的实现了一下目标:
        // 每次请求到来的时候获取下精确的纳秒时间 nanoTime. (纳秒随机开始可能获取到负数)
        // 因此针对不同的值不同处理。
        // 被意外唤醒那么等一会。
        // The code below is very delicate, to achieve these goals:
        // - call nanoTime exactly once for each call to park
        // - if nanos <= 0L, return promptly without allocation or nanoTime
        // - if nanos == Long.MIN_VALUE, don't underflow
        // - if nanos == Long.MAX_VALUE, and nanoTime is non-monotonic
        //   and we suffer a spurious wakeup, we will do no worse than
        //   to park-spin for a while
        long startTime = 0L;    // Special value 0L means not yet parked
        WaitNode q = null;
        boolean queued = false;
        for (;;) {
            int s = state;
            if (s > COMPLETING) {
                if (q != null)
                    q.thread = null;
                return s;
            }
            else if (s == COMPLETING)
                // We may have already promised (via isDone) that we are done
                // so never return empty-handed or throw InterruptedException
                Thread.yield();
            else if (Thread.interrupted()) {
                removeWaiter(q);
                throw new InterruptedException();
            }
            else if (q == null) {
                if (timed && nanos <= 0L)
                    return s;
                q = new WaitNode();  //用户方传入等待和时间两个参数 且时间>0 创建等待节点
            }
            else if (!queued)
                queued = U.compareAndSwapObject(this, WAITERS,
                                                q.next = waiters, q);
            else if (timed) {        // 等待 传入参数nanos
                final long parkNanos;
                if (startTime == 0L) { // first time
                    startTime = System.nanoTime();
                    if (startTime == 0L)
                        startTime = 1L;
                    parkNanos = nanos;
                } else { 
                    long elapsed = System.nanoTime() - startTime;
                    if (elapsed >= nanos) {
                        removeWaiter(q);
                        return state;
                    }
                    parkNanos = nanos - elapsed;
                }
                // nanoTime may be slow; recheck before parking 
                if (state < COMPLETING)
                    LockSupport.parkNanos(this, parkNanos);
            }
            else
                LockSupport.park(this);
        }
    }

这一堆看着是很长,发现注释只有一句话。
Awaits completion or aborts on interrupt or timeout.
这个函数就干了这个事情, 等完成、等取消、等取消、等超时。 有人可能会说胡说,明明只有三个return。
throw new InterruptedException();这不是还有一个抛出异常。
然后removeWaiter()又做了什么?

    /**
     * Tries to unlink a timed-out or interrupted wait node to avoid
     * accumulating garbage.  Internal nodes are simply unspliced
     * without CAS since it is harmless if they are traversed anyway
     * by releasers.  To avoid effects of unsplicing from already
     * removed nodes, the list is retraversed in case of an apparent
     * race.  This is slow when there are a lot of nodes, but we don't
     * expect lists to be long enough to outweigh higher-overhead
     * schemes.
     */
    private void removeWaiter(WaitNode node) {
        if (node != null) {
            node.thread = null;
            retry:
            for (;;) {          // restart on removeWaiter race
                for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
                    s = q.next;
                    if (q.thread != null)
                        pred = q;
                    else if (pred != null) {
                        pred.next = s;
                        if (pred.thread == null) // check for race
                            continue retry;
                    }
                    else if (!U.compareAndSwapObject(this, WAITERS, q, s))
                        continue retry;
                }
                break;
            }
        }
    }

3. FutureTask使用案例

ExecutorService service = Executors.newSingleThreadExecutor();
FutureTask futureTask = new FutureTask<>(new Callable() {
     @Override
     public String call() throws Exception {
         return "futureTask say HelloWorld!!!";
     }
});
service.execute(futureTask);
System.out.println(futureTask.get());

参考资料

[1] Unsafe : http://mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/
[2] synchronized 实现方式 : https://blog.csdn.net/qq_36520235/article/details/81176536
[3] objectMonitor.hpp 源码 : https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/da3a1f729b2b/src/share/vm/runtime/objectMonitor.hpp
[4] usafe https://blog.csdn.net/zyzzxycj/article/details/89877863

你可能感兴趣的:(FutureTask 可获取结果的异步请求)