Future cancel迷惑性boolean入参解析

 更新时间:2023年02月28日 16:49:13   作者:Code皮皮虾  
这篇文章主要为大家介绍了Future cancel迷惑性boolean入参解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

前言

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

但是cancel方法有一个boolean类型的入参,比较迷惑,之前也了解过该入参true 和 false的区别,但过一段时间之后就又忘了,遂写了本文进行记录,顺便了解下源码~

/**
 * Attempts to cancel execution of this task.  This attempt will
 * fail if the task has already completed, has already been cancelled,
 * or could not be cancelled for some other reason. If successful,
 * and this task has not started when {@code cancel} is called,
 * this task should never run.  If the task has already started,
 * then the {@code mayInterruptIfRunning} parameter determines
 * whether the thread executing this task should be interrupted in
 * an attempt to stop the task.
 *
 * <p>After this method returns, subsequent calls to {@link #isDone} will
 * always return {@code true}.  Subsequent calls to {@link #isCancelled}
 * will always return {@code true} if this method returned {@code true}.
 *
 * @param mayInterruptIfRunning {@code true} if the thread executing this
 * task should be interrupted; otherwise, in-progress tasks are allowed
 * to complete
 * @return {@code false} if the task could not be cancelled,
 * typically because it has already completed normally;
 * {@code true} otherwise
 */
boolean cancel(boolean mayInterruptIfRunning);

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

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

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

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

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

FutureTask任务状态认知

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

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

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

但是通过对上面状态定义的了解,我们可以发现,在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入参解析的详细内容,更多关于Future cancel boolean入参的资料请关注脚本之家其它相关文章!

相关文章

  • Spring Framework远程代码执行漏洞分析(最新漏洞)

    Spring Framework远程代码执行漏洞分析(最新漏洞)

    Spring Framework 是一个开源应用框架,旨在降低应用程序开发的复杂度,它具有分层体系结构,允许用户选择组件,同时还为 J2EE 应用程序开发提供了一个有凝聚力的框架,对Spring远程代码执行漏洞相关知识感兴趣的朋友一起看看吧
    2022-04-04
  • Java使用Arrays.sort()方法实现给对象排序

    Java使用Arrays.sort()方法实现给对象排序

    这篇文章主要介绍了Java使用Arrays.sort()方法实现给对象排序,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • 在IDEA中创建Web项目的详细过程

    在IDEA中创建Web项目的详细过程

    这篇文章主要给大家介绍了关于在IDEA中创建Web项目的详细过程,很多朋友可能在学习java基础的时候已经熟练掌握了IDEA创建java项目的基本步骤,但随着学习技术的不断深入,不同的IDEA版本可能在项目的创建页面上出现些许的出入,需要的朋友可以参考下
    2023-10-10
  • javaweb中ajax请求后台servlet(实例)

    javaweb中ajax请求后台servlet(实例)

    下面小编就为大家带来一篇javaweb中ajax请求后台servlet(实例)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • Java多线程Atomic包操作原子变量与原子类详解

    Java多线程Atomic包操作原子变量与原子类详解

    这篇文章主要介绍了Java多线程Atomic包操作原子变量与原子类详解,简单介绍了Atomic,同时涉及java.util.concurrent中的原子变量,Atomic类的作用等相关内容,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • JavaWeb 入门:Hello Servlet

    JavaWeb 入门:Hello Servlet

    这篇文章主要介绍了Servlet开发JavaWeb工程示例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-07-07
  • java 中数组初始化实例详解

    java 中数组初始化实例详解

    这篇文章主要介绍了 本文主要讲数组的初始化方法、可变参数列表以及可变参数列表对函数重载的影响的相关资料,需要的朋友可以参考下
    2017-05-05
  • 使用Mybatis的Batch Insert Support 实现批量插入

    使用Mybatis的Batch Insert Support 实现批量插入

    这篇文章主要介绍了使用Mybatis的Batch Insert Support 实现批量插入。具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • SpringBoot + Shiro前后端分离权限

    SpringBoot + Shiro前后端分离权限

    这篇文章主要为大家详细介绍了SpringBoot + Shiro前后端分离权限,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-12-12
  • Java泛型之上界下界通配符详解

    Java泛型之上界下界通配符详解

    这篇文章主要介绍了Java泛型之上界下界通配符详解,学习使用泛型编程时,更令人困惑的一个方面是确定何时使用上限有界通配符以及何时使用下限有界通配符。本文提供一些设计代码时要遵循的一些准则。,需要的朋友可以参考下
    2019-06-06

最新评论