FutureTask的使用以及源码剖析

8 篇文章 0 订阅
4 篇文章 0 订阅

FutureTask类图

君欲善其事,必先利其器,咱们要想搞清楚RxJava的线程调度器,这章是基础,先补充下基础知识。
我们首先来画一下类图,好分析这个类的逻辑流程以及功能:
在这里插入图片描述
从图中可以知道,它继承了runnable接口以及future接口,那么说明这个类可以用来执行线程的异步任务,也可以用来将异步结果同步接受。

  public static void main() {
        ExecutorService executor = Executors.newCachedThreadPool();
        Task task = new Task();
        Future<Integer> result = executor.submit(task);
        executor.shutdown();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
        System.out.println("主线程在执行任务");
        try {
            System.out.println("task运行结果"+result.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("所有任务执行完毕");
    }
class Task implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("子线程在进行计算");
        Thread.sleep(3000);
        int sum = 0;
        for(int i=0;i<100;i++)
            sum += i;
        return sum;
    }
}

运行打印的结果如下:

I/System.out: 子线程在进行计算
I/System.out: 主线程在执行任务
I/System.out: task运行结果4950
I/System.out: 所有任务执行完毕

我们知道FutureTask这个类的作用就是可以让耗时操作的结果可以以同步的形式返回。上面的代码就是建立一个线程池,然后将我们的Task传入线程池,然后返回了一个Future result 对象。这个返回的是FutureTask的接口,实际上还是futureTask对象。
后面就可以直接调用result.get()获取返回结果。

FutureTask有两种创建方式:
第一种:

    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
    }
    
    //适配器模式将参数包装成callable对象
    public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
    }

run方法

既然我们FutureTask是个runnable接口,所以一定是从run()方法开始分析了,代码如下:


    public void run() {
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    ..省略
                }
                if (ran)
                    set(result);
            }
        } finally {
        	...省略
        }
    }

就是在线程中调用我们的callable.call()方法,将result返回,使用的是set(result)方法去设置返回值。我们来看下set方法是怎么做的:

protected void set(V v) {
    if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
        outcome = v;
        U.putOrderedInt(this, STATE, NORMAL); // final state
        finishCompletion();
    }
}

首先就是if判断,它的意思就是将state从NEW设置成COMPLETING状态(CAS乐观锁操作,有兴趣自己了解下),接下来如果if成立的话,将结果设置给outcome变量,接着将状态设置成NORMAL(一样的CAS操作)。并且调用finishCompletion()方法。

 private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
            if (U.compareAndSwapObject(this, WAITERS, 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是所有调用了get()方法之后处于阻塞状态的线程链表,这里是将该链表上所有的节点都摘下里,然后置空,帮助内存回收。run方法的分析基本上就告一段落了。

get方法

futureTask的目的就是为了能够同步的将耗时操作的结果返回,所以我们从调用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);
}

从上面的方法可以看得到,首先是判断if判断当前的状态是否是小于等于COMPLETE状态的,是的话进入waitDone()方法。
看了waitDone方法的源码:

private int awaitDone(boolean timed, long nanos)
    throws InterruptedException {
    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)
            Thread.yield();
        else if (Thread.interrupted()) {
            removeWaiter(q);
            throw new InterruptedException();
        }
        else if (q == null) {
            if (timed && nanos <= 0L)
                return s;
            q = new WaitNode();
        }
        else if (!queued)
            queued = U.compareAndSwapObject(this, WAITERS,
                                            q.next = waiters, q);
        else if (timed) {
        	...省略  
        }
        else
            LockSupport.park(this);
   }
}

大概意思就是for循环,一直查询状态state是否大于COMPLETING状态,是的话直接返回结果;如果是等于的,那么就yield(),让出时间片,再次循环;如果是小于的话,那么会创建waitNode将节点加入到队列中。那么这些操作都做了的话,最后还是没有得到完成的结果的话,就调用最后一个else语句 LockSupport.park(this); 布尔类型timed这个不会成立的,参数就是false,这个是用于请求超时功能的调用的。
LockSupport.park(this)这个函数可以直接把它当成是await,对应的unpark相当于notify,这是新的锁机制,性能比原来的要好。
这里也就是将当前调用get的线程切换成等待状态,一直阻塞在这里。
在这里插入图片描述
前面我们有一段是finishCompletion方法的调用,也就是在结果产生之后,会调用unpark函数,让所有的线程都唤醒,然后继续进行for循环获取结果值。那么futureTask这个类分析就基本上结束了,功能和原理应该是很清晰了。

最后上一下,整个过程的流程图:
在这里插入图片描述
再补充一下,futureTask状态转化图:

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

其中第三种转移图,是当线程正在执行的时候,用户调用了cancle方法,让线程从NEW状态转化成CANCELLED状态或者INTERRUPTING状态,最终会转成INTERRUPTED状态。此时运行该Task的线程会被中断。

   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;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值