您好,登录后才能下订单哦!
本篇内容介绍了“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方法,会尝试取消任务的执行,但如果任务已经完成、已经取消或其他原因无法取消,则尝试取消任务失败。
如果取消成功,并且在取消时
该任务还未执行,那么这个任务永远不会执行。
如果该任务已经启动,那么会根据cancel的boolean入参来决定是否中断执行此任务的线程来停止任务。
通过注释我们大致能了解到cancel的一个作用,但是还不够细致,接下来我们通过源码解读详细的带大家了解一下~
首先,我们先了解下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中并没有一个表明任务处于执行中的一个状态!
直接看FutureTask的run方法源码
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源码
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嘛,这好像没啥用啊?
当然不是,此时我们需要再回头看看FutureTask的run方法
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入参源码分析”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。