返回顶部
首页 > 资讯 > 精选 >Future cancel迷惑性boolean入参源码分析
  • 236
分享到

Future cancel迷惑性boolean入参源码分析

2023-07-05 07:07:34 236人浏览 八月长安
摘要

本篇内容介绍了“Future cancel迷惑性boolean入参源码分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!前言当我们

本篇内容介绍了“Future cancel迷惑性boolean入参源码分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

前言

当我们使用线程池submit一个任务后,会返回一个Future,而在Future接口中存在一个cancel方法,来帮助我们取消掉任务。

但是cancel方法有一个boolean类型的入参,比较迷惑,之前也了解过该入参true 和 false的区别。

boolean cancel(boolean mayInterruptIfRunning);

上面是cancel方法的接口定义,当然英文看着麻烦,咱直接翻译成看得懂的~

cancel方法,会尝试取消任务的执行,但如果任务已经完成、已经取消或其他原因无法取消,则尝试取消任务失败。

如果取消成功,并且在取消时

  • 该任务还未执行,那么这个任务永远不会执行。

  • 如果该任务已经启动,那么会根据cancelboolean入参来决定是否中断执行此任务的线程来停止任务。

通过注释我们大致能了解到cancel的一个作用,但是还不够细致,接下来我们通过源码解读详细的带大家了解一下~

FutureTask任务状态认知

首先,我们先了解下FutureTask中对任务状态的定义

在使用线程池submit后,实际上是返回的一个FutureTask,而FutureTask中对于任务定义了以下状态,并且在注释中,也定义了状态的流转过程~

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;

但是通过对上面状态定义的了解,我们可以发现,在FutureTask中并没有一个表明任务处于执行中的一个状态!

直接看FutureTaskrun方法源码

public void run() {    if (state != NEW ||        !RUNNER.compareAndSet(this, 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 {      //... 省略    }}protected void setException(Throwable t) {  if (STATE.compareAndSet(this, NEW, COMPLETING)) {    outcome = t;    STATE.setRelease(this, EXCEPTIONAL); // final state    finishCompletion();  }}protected void set(V v) {  if (STATE.compareAndSet(this, NEW, COMPLETING)) {    outcome = v;    STATE.setRelease(this, NORMAL); // final state    finishCompletion();  }}

通过上面源码,我们也能了解到

  • 当任务正常执行完毕时,任务状态流转: NEW -> COMPLETING -> NORMAL

  • 任务执行异常时,任务状态流转: NEW -> COMPLETING -> EXCEPTIONAL

所以,当任务刚创建,或者是任务在执行过程中,任务的状态都是NEW

cancel源码分析

此时再来分析cancel源码

public boolean cancel(boolean mayInterruptIfRunning) {    // NEW为新建或者运行态    // 1. 此时任务已经不是NEW,说明要么是完成要么是异常,取消不了,所以返回false    // 2. 此时任务还是NEW,如果我们传入true,则CAS标记任务为INTERRUPTING,否则是CANCELLED    // 防止并发取消任务,CAS只会有一个线程成功,其余线程失败    if (!(state == NEW && STATE.compareAndSet          (this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))        return false;    try {            // 传入true,则打断该任务的执行线程        if (mayInterruptIfRunning) {            try {                Thread t = runner;                if (t != null)                    t.interrupt();            } finally {                // 比较任务状态为INTERRUPTED                STATE.setRelease(this, INTERRUPTED);            }        }    } finally {        finishCompletion();    }    return true;}

通过对FutureTask任务状态的认知,再结合对cancel源码的分析

我们可以总结出以下结论

当任务已经完成或者异常时,无法取消任务

任务处于新建或者运行状态时

cancel方法入参传入true

将任务状态NEW -> INTERRUPTING -> INTERRUPTED,并打断执行该任务的线程

cancel方法入参传入false

将任务状态NEW -> CANCELLED

但有个问题,传入false只是将状态从NEW变成CANCELLED嘛,这好像没啥用啊?

当然不是,此时我们需要再回头看看FutureTaskrun方法

public void run() {    if (state != NEW ||        !RUNNER.compareAndSet(this, 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 {      //... 省略    }}

run方法开头我们可以看到,如果任务的状态不是NEW,那么会直接return,不执行任务

那此时再想想传入false将任务状态从NEW -> CANCELLED,是不是当任务还没有开始执行时,我们cancel(false)就可以取消掉未执行的任务了~

总结

通过上面的源码解读,我们大致能了解了cancel的机制,但是我们还是完善的总结一下

任务如果不是NEW状态是不会执行的

cancel取消任务会改变任务的状态

  • 如果传入true, 则将任务状态NEW -> INTERRUPTING -> INTERRUPTED,并打断执行该任务的线程

  • 如果传入false,将任务状态NEW -> CANCELLED

传入false只能取消还未执行的任务

传入true,能取消未执行的任务,能打断正在执行的任务

扩展知识点

cancel源码中,我们可以看到finally中会去调用finishCompletion

那么,finishCompletion是干啥的呢?

private void finishCompletion() {    // assert state > COMPLETING;    for (Waitnode q; (q = waiters) != null;) {        // 原子性将WAITERS设置为null        if (WAITERS.weakCompareAndSet(this, q, null)) {                      // 遍历WAITERS,将阻塞的线程都唤醒            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;                q = next;            }            break;        }    }    // 扩展方法,交给自己实现    done();    callable = null;}

大家可以想想,当我们submit一个任务时,一般情况下都会需要去获取他的返回值,会调用get方法进行阻塞获取。在FutureTask中,会维护一条链表,该链表记录了等待获取该任务返回值被阻塞的线程,在调用get方法时,会将组装waiters链表。所以,当我们取消一个任务时,是不是也应该去将阻塞等待获取该任务的所有线程进行唤醒,而finishCompletion方法就是做这个事情的~

“Future cancel迷惑性boolean入参源码分析”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

--结束END--

本文标题: Future cancel迷惑性boolean入参源码分析

本文链接: https://lsjlt.com/news/350328.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作