FutureTask源码解读——阻塞获取异步计算结果(阻塞、取消、装饰器

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

Runnable会被包装成一个实现了CallableRunnableAdapter赋值给callable。适配的过程也可以看出result是怎么传进去就会怎么返回。

//Executors

public static Callable callable(Runnable task, T result) {

if (task == null)

throw new NullPointerException();

//将Runnable适配成Callable

return new RunnableAdapter(task, result);

}

//适配器、典型的适配器模式

static final class RunnableAdapter implements Callable {

final Runnable task;

final T result;

RunnableAdapter(Runnable task, T result) {

this.task = task;

this.result = result;

}

public T call() {

task.run();

//传入的result并没有做任何处理

return result;

}

}

所以官方给的函数AbstractExecutorService#submit(java.lang.Runnable, T),也可以传入Runnable类型的任务和result,不过只能判断任务是否完成或者取消任务,外部getresult还是传进去的result,没有太大的意义。

3、核心函数

(1)get阻塞获取结果

get()有两种,一种是会等待计算结束返回,一种是加了超时时长timeout,get线程等待timeout时长,如果此时还没有完成就抛出异常TimeoutException

/**

  • 会一直阻塞到计算结束,可以被打断

  • @throws CancellationException {@inheritDoc}

*/

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

}

private V report(int s) throws ExecutionException {

Object x = outcome;

if (s == NORMAL)

//如果是正常结束,返回结果

return (V)x;

//取消或者中断的就抛出取消异常

if (s >= CANCELLED)

throw new CancellationException();

//其他状态的抛异常EXCEPTIONAL

throw new ExecutionException((Throwable)x);

}

awaitDone阻塞等待

阻塞的核心代码就是awaitDone,get时若正在计算,将会被放入等待栈阻塞,直到超时时间到,或者被唤醒,或者被中断,然后返回当前的状态。

  1. 当前get线程被打断,删除等待节点,并抛出InterruptedException

  2. 若此时s > COMPLETING有可能完成、取消、中断,将等待节点(WaitNode)的thread设置为null,并返回当前状态。

  3. s == COMPLETING说明正在完成,暂停当前get线程,让出对cpu的占用,执行其他get线程。

  4. 若当前get线程还没有入等待栈,实例化一个WaitNode,并cas入栈。

  5. 最后阻塞,阻塞分为时间阻塞和永久阻塞,阻塞时间到,删除等待节点并返回当前状态。

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

//正在完成,暂停当前线程,执行其他get线程

Thread.yield();

else if (q == null)

//NEW 状态,新建一个WaitNode

q = new WaitNode();

else if (!queued)

//cas入栈

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

//timed false 会一直阻塞到 计算完成, 需要唤醒

LockSupport.park(this);

}

}

/**

  • 栈,node.thread=null的节点会被删除

  • @param node

*/

private void removeWaiter(WaitNode node) {

if (node != null) {

node.thread = null;

retry:

for (;😉 {

// restart on removeWaiter race

//循环删除node.thread = null的节点

for (WaitNode pred = null, q = waiters, s; q != null; q = s) {

s = q.next;

if (q.thread != null)

pred = q;

else if (pred != null) {

pred.next = s;

if (pred.thread == null) // check for race

continue retry;

}

else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,

q, s))

continue retry;

}

break;

}

}

}

(2)cancel取消任务中断工作线程

调用cancel()可能是被中断(mayInterruptIfRunning=true)或者是主动取消(mayInterruptIfRunning=false)。

  • 中断取消,设置stateINTERRUPTING,并调用runner.interrupt()中断当前运行线程,设置stateINTERRUPTED,最后清空等待栈中所有阻塞节点并唤醒所有等待的线程。

  • 主动取消,设置stateCANCELLED,最后清空等待栈中所有阻塞节点并唤醒所有等待的线程。

从代码可以看出只有stateNEW才能被取消。

public boolean cancel(boolean mayInterruptIfRunning) {

if (!(state == NEW &&

//cass设置 state为INTERRUPTING or CANCELLED

UNSAFE.compareAndSwapInt(this, stateOffset, NEW,

mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))

//状态没有修改成功返回false,取消失败

return false;

try { // in case call to interrupt throws exception

//如果是被打断的 就调用t.interrupt();中断线程

if (mayInterruptIfRunning) {

try {

Thread t = runner;

if (t != null)

//中断当前线程

t.interrupt();

} finally { // final state

//设置state为中断

UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);

}

}

} finally {

//最后 删除并唤醒所有等待的线程

finishCompletion();

}

return true;

}

//删除并唤醒所有等待的线程

private void finishCompletion() {

// assert state > COMPLETING;

for (WaitNode q; (q = waiters) != null;) {

//循环cas WaitNode 为null,删除所有等待的线程

if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {

for (;😉 {

Thread t = q.thread;

if (t != null) {

q.thread = null;

//循环唤醒唤醒

LockSupport.unpark(t);

}

WaitNode next = q.next;

if (next == null)

//最后了,break

break;

q.next = null; // unlink to help gc 设置为null 有利于gc

q = next;

}

break;

}

}

//钩子函数

done();

callable = null; // to reduce footprint

}

(3)run执行任务代码并唤醒所有等待线程

FutureTask是会被传给ThreadPoolExecutor.Worker,由线程池启动工作线程,然后调用FutureTaskrun()函数,run()又调用callable.call()run()会将结果设置给outcome

public void run() {

//新任务–>执行UNSAFE.compareAndSwapObject(this, runnerOffset,

// null, Thread.currentThread()))

//新任务会将当前线程设置给runner 这里的作用是乐观锁,保证下面的执行流程是只有一个线程

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 {

//调用callable的call,并设置返回值

//如果传进来的任务是Runnable,会被转换成callable

result = c.call();

//若运行异常,ran=false,异常会被捕获处理

//所以传进来的任务的run或者call代码块最好try-catch下

ran = true;

} catch (Throwable ex) {

result = null;

ran = false;

//设置异常给outcome

setException(ex);

}

if (ran)

//运行正常完成,设置结果给outcome

set(result);

}

} finally {

// runner must be non-null until state is settled to

// prevent concurrent calls to run()

//最后 runner=null 相当于是释放锁

runner = null;

// state must be re-read after nulling runner to prevent

// leaked interrupts

int s = state;

if (s >= INTERRUPTING)

//如果状态是被打断,让出cpu

handlePossibleCancellationInterrupt(s);

}

}

执行细节如下:

  • 新任务CAS设置当前线程给runner,这里的作用是乐观锁保证下面的执行流程只有一个线程,并且外部可以通过runner随时中断执行。

  • 直接调用callable.call()执行任务代码。

  • 中途出现异常,将异常设置给outcome,状态流转为异常完成,并唤醒所有等待的线程。

protected void setException(Throwable t) {

if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {

//设置异常

outcome = t;

//设置异常状态

UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state

//唤醒所有线程

finishCompletion();

}

}

  • 正常运行完毕将运行结果result设置给outcome,状态流转为正常完成,并唤醒所有等待的线程。

protected void set(V v) {

if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {

//设置为完成状态

outcome = v;

//设置正常状态

UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state

//完成操作-删除并唤醒所有等待的线程

finishCompletion();

}

}

private void finishCompletion() {

// assert state > COMPLETING;

for (WaitNode q; (q = waiters) != null;) {

//循环cas WaitNode 为null,删除所有等待的线程

//这里cas删除 其实是为了if里面的操作线程安全无锁

if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {

for (;😉 {

Thread t = q.thread;

if (t != null) {

q.thread = null;

//循环唤醒唤醒

LockSupport.unpark(t);

}

WaitNode next = q.next;

if (next == null)

//最后了,break

break;

q.next = null; // unlink to help gc 设置为null 有利于gc

q = next;

}

break;

}

}

//钩子函数

done();

callable = null; // to reduce footprint

}

  • 最终runner设置为null,相当于释放锁;如若此时状态为被打断状态INTERRUPTING,需要让出cpu

private void handlePossibleCancellationInterrupt(int s) {

// It is possible for our interrupter to stall before getting a

// chance to interrupt us. Let’s spin-wait patiently.

if (s == INTERRUPTING)

while (state == INTERRUPTING)

//暂停当前线程,让出cpu时间片

Thread.yield(); // wait out pending interrupt

}

(4)runAndReset重复执行

runAndReset()run()类似,但是没有把result设置给outcome,函数返回为boolean,用于是否重复执行。

protected boolean runAndReset() {

if (state != NEW ||

!UNSAFE.compareAndSwapObject(this, runnerOffset,

null, Thread.currentThread()))

return false;

boolean ran = false;

int s = state;

try {

最后

我还通过一些渠道整理了一些大厂真实面试主要有:蚂蚁金服、拼多多、阿里云、百度、唯品会、携程、丰巢科技、乐信、软通动力、OPPO、银盛支付、中国平安等初,中级,高级Java面试题集合,附带超详细答案,希望能帮助到大家。

新鲜出炉的蚂蚁金服面经,熬夜整理出来的答案,已有千人收藏

还有专门针对JVM、SPringBoot、SpringCloud、数据库、Linux、缓存、消息中间件、源码等相关面试题。

新鲜出炉的蚂蚁金服面经,熬夜整理出来的答案,已有千人收藏

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

lean`,用于是否重复执行。

protected boolean runAndReset() {

if (state != NEW ||

!UNSAFE.compareAndSwapObject(this, runnerOffset,

null, Thread.currentThread()))

return false;

boolean ran = false;

int s = state;

try {

最后

我还通过一些渠道整理了一些大厂真实面试主要有:蚂蚁金服、拼多多、阿里云、百度、唯品会、携程、丰巢科技、乐信、软通动力、OPPO、银盛支付、中国平安等初,中级,高级Java面试题集合,附带超详细答案,希望能帮助到大家。

[外链图片转存中…(img-6FyTfKL8-1713600667162)]

还有专门针对JVM、SPringBoot、SpringCloud、数据库、Linux、缓存、消息中间件、源码等相关面试题。

[外链图片转存中…(img-8YyZjvPH-1713600667163)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-M9GmQPqi-1713600667163)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,可以使用FutureTask类来等待异步执行结果。具体步骤如下: 1. 定义一个Callable接口的实现类,用于执行需要获取结果集的操作,并返回结果。 2. 创建一个FutureTask对象,将Callable实现类实例作为参数传递给FutureTask构造函数。 3. 使用Executor框架(例如ThreadPoolExecutor)来执行FutureTask对象,异步执行操作并返回Future对象。 4. 在主线程中,使用Future.get()方法获取异步操作的结果。该方法会阻塞当前线程,直到异步操作完成并返回结果。 下面是一个简单的示例代码: ```java import java.util.concurrent.*; public class FutureTaskExample { public static void main(String[] args) throws Exception { // 创建一个Callable实现类的实例 Callable<Integer> task = new Callable<Integer>() { public Integer call() { // 模拟异步操作 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } return 100; } }; // 创建FutureTask对象,将Callable实现类实例作为参数传递给FutureTask构造函数 FutureTask<Integer> futureTask = new FutureTask<Integer>(task); // 创建线程池并执行FutureTask对象 Executor executor = Executors.newSingleThreadExecutor(); executor.execute(futureTask); // 在主线程中,使用Future.get()方法获取异步操作的结果 Integer result = futureTask.get(); System.out.println("异步操作的结果是:" + result); } } ``` 在上面的示例代码中,创建了一个Callable实现类的实例,并将其传递给FutureTask构造函数。然后,通过线程池异步执行FutureTask对象,并在主线程中使用Future.get()方法获取异步操作的结果。注意,Future.get()方法会阻塞当前线程,直到异步操作完成并返回结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值