Future使用的这个坑你知道吗?

Future使用的这个坑你知道吗?

@Test
public void futureWithException() {
	/*   newFixedThreadPool等价于     return new ThreadPoolExecutor(nThreads, nThreads, 
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());*/
	// 核心线程数和最大线程数都是1
    Future<Object> future = Executors.newFixedThreadPool(1).submit(() -> {
        System.out.println("即将抛出异常");
        throw new RuntimeException();
    });
    while (!future.isDone()) ;
}
// 没有异常抛出! 风平浪静!
image-20210113185939219

咦? 我的RuntimeException()异常咧?

那怎么才能抛出异常呢!

我做了下测试,发现Future如果没有调用get()方法不会抛出异常

@Test
public void futureWithException() {
    Future<Object> future = Executors.newFixedThreadPool(1).submit(() -> {
        System.out.println("即将抛出异常");
        throw new RuntimeException();
    });
    while (!future.isDone()) ;
    try {
        future.get();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

在这里插入图片描述

我不禁好奇? 为啥咧?

经过我深入观察发现: 其实这个不是Future的锅,是java.util.concurrent.ThreadPoolExecutor的问题!
在这里插入图片描述

ThreadPoolExecutor的submit使用了FutureTask:

public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task); //此处返回FutureTask
    execute(ftask);
    return ftask;
}

让我们再看看FutureTask的run方法(任务执行时就是取执行run()方法)

public void run() {
   ...//其他代码
    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;
                // 如果call执行过程抛异常了; FutureTask的操作就是setException(ex)了事
                setException(ex);
            }
            if (ran)
                set(result);
        }
    } finally {
	     .....// 其他代码
    }
}

深入看看setException()方法

protected void setException(Throwable t) { 
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { //使用CAS修改FutureTask的状态
        outcome = t; // 异常记录到这里来了
        //UNSAFE.putOrderedInt逻辑上等价于UNSAFE.putInt
        UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // 再设置为EXCEPTIONAL
        // 信号相关的东西,暂时不管
        finishCompletion();
    }
}

至于为什么get()会抛出异常:

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING) // EXCEPTIONAL>COMPLETING
        s = awaitDone(false, 0L);
    return report(s);
} // 好吧, 平平无奇, 让我们深入看看report()方法

private V report(int s) throws ExecutionException {
    Object x = outcome;  // 阿这,破案了。(还记得outcome吗,这个就是之前在run中设置的异常呀!)
    if (s == NORMAL)
        return (V) x; // 抛出异常
    if (s >= CANCELLED)
        throw new CancellationException();// 抛出异常
    throw new ExecutionException((Throwable) x);// 抛出异常(包装下重新抛出)
}

java.util.concurrent.FutureTask#get(long, java.util.concurrent.TimeUnit)也会调用report方法哦!

结案了!
在这里插入图片描述

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值