FutureTask源码初探

image

从上图可以看出,FutureTask实现了RunnableFuture接口,而RunnableFuture则继承了Runnable和Future接口。由于Runnalbe接口大家都比较熟悉,因此,咱们就从Future说起。

Future初探

Future是什么?从java语言本身来讲,它就是一个简单的接口,定义了如下几个方法。


boolean cancel(boolean mayInterruptIfRunning);

boolean isCancelled();

boolean isDone();

V get()throws InterruptedException, ExecutionException;

V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;

那么这个接口的用途是什么呢?从这个类的注释就可以看出:Future代表了异步计算的结果。

也就是说,可以通过Future的实现类封装异步计算的结果,然后可以通过get方法获取计算结果、通过cancle取消异步计算,也可以通过isCanclled、isDone等方法来判断此次计算是否已经被取消或者执行完毕。

FutureTask

FutureTask是目前Future接口的实现类,下面我们看下FutureTask的实现。

主要看一下get方法,其用来获取计算结果,当计算没完成时,则等待程序执行完成。

public V get()throws InterruptedException, ExecutionException {

int s =state;

    if (s <=COMPLETING)

s = awaitDone(false, 0L);

    return report(s);

}

逻辑很简单,如果状态<=COMPLETING,则等待任务执行完成,然后获取执行结果,返回。

重点看一下awaitDone方法:

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

    }

}

暂且不关心任务被中断的情况。上述代码根据state字段进行逻辑控制:

如果>COMPLETING,表明任务执行成功,除了返回状态外,如果WaitNode不为空,还将其thread置为null,为何这么操作,后面再来分析;

如果当前任务正在COMPLETING状态,执行yield方法,释放cpu时间片。之所以执行yield方法,而不是sleep方法,是因为COMPLETING到NOMAL时间较短,通过使线程转为就绪状态,相对于sleep方法使线程变为阻塞状态,效率更高。

如果状态为未完成,则进行一次循环,如果一次循环后,还是未完成状态,则通过UNSAFE.compareAndSwapObject方法,将当前线程加入到WaitNode中:

UNSAFE.compareAndSwapObject(this, waitersOffset, q.next =waiters, q);

compareAndSwapObject的意思为:通过cas方法,当前对象waiters属性等于waiters,让waiters属性等于q。

如果入队后,还是未完成状态,则调用LockSupport.park方法,阻塞当前线程。

简单介绍一下LockSupport。park方法会阻塞当前线程,unpark函数则是将指定线程唤醒。与Object的wait/notify相比,优势在于操作更精准,可以准确地唤醒某一个线程。

既然状态这么重要,咱们就看看FutureTask的状态是怎么变更的。

首先是新建任务:

public FutureTask(Callable callable) {

if (callable ==null)

throw new NullPointerException();

    this.callable = callable;

    this.state =NEW;      // ensure visibility of callable

}

不如是通过哪个构造方法,state都置为NEW。

再看看run方法执行对于state的影响:

public void run() {

if (state !=NEW ||

!UNSAFE.compareAndSwapObject(this, runnerOffset,

                                    null, Thread.currentThread()))

return;

    try {

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

    }

}

如果非NEW状态,则直接返回;

如果执行线程不为空,表明已经有线程在执行,直接返回,否则将runner置为当前线程

调用Callable的call方法,执行成功之后,将状态置为COMPLETING,然后将outcome设置为执行结果,再讲状态置为NORMAL。

下面重点来了:

finishCompletion方法,会遍历watiers,逐个调用unpark方法,唤醒上面get方法中被阻塞的线程。

总结

到这里,核心流程就分析完了。总结一下。

Callable通过增加返回值,解决了以往线程无法有返回值的问题。而Future接口,则是为了获取Callable的响应值。其核心逻辑为,调用get方法时,如果Callable任务没执行完,则阻塞get方法,直至任务执行完成。FutureTask为Future的实现类,其通过LockSupport的park和unpark,提升get的效率。FutureTask还实现了Runnable接口,这样其除了可以通过ExecuteService执行,还可以用过Thread来进行执行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353