大家好,我是烤鸭:

​     今天看一下 FutureTask源码。好吧,其实遇到问题了,哪里不会点哪里。

伪代码

package src.executor;import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.*;/***@program: *@description: 测试*@author:  *@email: *@create: 2021/07/07 11:35*/
public class FutureAndLatchTest {static ThreadPoolTaskExecutor initTaskPool(){ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();taskExecutor.setCorePoolSize(15);taskExecutor.setMaxPoolSize(60);taskExecutor.setQueueCapacity(200);taskExecutor.setKeepAliveSeconds(60);taskExecutor.setThreadNamePrefix("test-");taskExecutor.setWaitForTasksToCompleteOnShutdown(true);taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());taskExecutor.setAwaitTerminationSeconds(60);taskExecutor.initialize();return taskExecutor;}public static void main(String[] args) {ThreadPoolTaskExecutor taskPool = initTaskPool();for (int i = 0; i < 20; i++) {CountDownLatch latch = new CountDownLatch(2);Future<Integer> f1 = taskPool.submit(() ->future(latch));Future<Integer> f2 = taskPool.submit(() ->future(latch));try {latch.await(200, TimeUnit.MILLISECONDS);} catch (InterruptedException e) {e.printStackTrace();}if(!f1.isDone()){System.out.println("f1 is not done");}if(!f2.isDone()){System.out.println("f2 is not done");}}System.out.println("taskPool finish");}private static Integer future(CountDownLatch latch) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}finally {latch.countDown();}return 1;}
}

这段代码维护了一个线程池,执行两个线程用CountDownLatch做超时控制,再判断线程是否完成,这段代码会输出 is not done么。看下实际结果。(有概率复现)

输出:
f1 is not done
f1 is not done
f2 is not done
f1 is not done
f1 is not done
f2 is not done
f1 is not done
taskPool finish

原因分析

看一下 Future 的方法注释,就是方法是否执行完成,理论上没问题啊。

/*** Returns {@code true} if this task completed.** Completion may be due to normal termination, an exception, or* cancellation -- in all of these cases, this method will return* {@code true}.** @return {@code true} if this task completed*/
boolean isDone();

而实现调用的 FutureTask

public boolean isDone() {return state != NEW;
}

出现这个state还得再看下源码,state用来维护线程状态的,注释也说明了几种状态的流转。

/*** The run state of this task, initially NEW.  The run state* transitions to a terminal state only in methods set,* setException, and cancel.  During completion, state may take on* transient values of COMPLETING (while outcome is being set) or* INTERRUPTING (only while interrupting the runner to satisfy a* cancel(true)). Transitions from these intermediate to final* states use cheaper ordered/lazy writes because values are unique* and cannot be further modified.** 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;

新建 -> 进行中 -> 完成、新建 -> 进行中 -> 异常、新建 -> 取消、新建 ->等待 ->取消

更多详细的可以看下这篇文章。

https://blog.csdn.net/qq_35067322/article/details/104872102

看下2012年的这个提问吧,和有位大神的回复。

https://stackoverflow.com/questions/9604713/future-isdone-returns-false-even-if-the-task-is-done

简单来说,就是子线程里调用finally 执行 countdownlatch.countdown()的时候,主线程发现 latch 变成0了就继续执行,但是这个时候 futureTask还在finally里,state没变过来。就是毫秒级别的线程切换,主线程在那一瞬间优先执行。

优化

原来代码里是想监听多线程的执行结果,执行完成后再去执行其他的操作。怎么样才能监听到实际结果呢,改为 Future.get();

try {f1.get(5,TimeUnit.MILLISECONDS);f2.get(5,TimeUnit.MILLISECONDS);
} catch (Exception e) {e.printStackTrace();
}

get 方法为啥没问题呢,看下源码。

/*** @throws CancellationException {@inheritDoc}*/
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);
}

状态没完成,就等他完成就好了。妥妥的CAS 乐观锁实现。

/*** Awaits completion or aborts on interrupt or timeout.** @param timed true if use timed waits* @param nanos time to wait, if timed* @return state upon completion*/
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 yetThread.yield();else if (q == null)q = new WaitNode();else if (!queued)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);}elseLockSupport.park(this);}
}

属实是有点水了…

FutureTask isDone 返回 false相关推荐

  1. java List集合中contains方法总是返回false

    ArrayList的contains方法 java 今天在用ArrayList类的caontains方法是遇到了问题,我写了一个存放User类的ArrayList 但在调用list.contains( ...

  2. ajax 阻止默认提交,jQuery验证插件:在对ajax调用servlet时,submitHandler不会阻止默认提交-返回false无效...

    我有一个使用jquery和servlet的简单表单.jQuery对Servlet进行Ajax调用,然后Servlet进行一些服务器端计算,然后通过jQuery在同一页面上显示结果.我不希望表单进行默认 ...

  3. torch.cuda.is_available()返回false

    torch.cuda.is_available()返回false 解决方法:已经安装torch的: import platformimport torchsysstr = platform.syste ...

  4. FTPClient.storeFile返回false的原因

    FTPClient.storeFile()返回false的原因 Debug搞了一晚上,什么都看过了,最后总算是自己茅塞顿开发现了问题. FTPClient会返回false的原因有很多, 首先有编码错误 ...

  5. yii2 设置的缓存无效,返回false,不存在

    为了那些因为标题点进来的小伙伴,我直接把问题解决方案写在开头: 问题描述, $cache->add($key,'value',1800);这样设置了值后,后面无论怎么取这个$key,取出来的结果 ...

  6. JavaScript 技术篇-chrome浏览器读取剪切板命令document.execCommand(‘paste‘)返回false原因及解决方法

    新版本 chrome 执行 document.execCommand('paste') 返回 false 因为读取剪切板涉及用户隐私安全,必须的用户允许的情况下可以进行访问,但是复制和剪切功能可以使用 ...

  7. IOS下,javascript 自带的 confirm 首次必返回 false

    前言 下面的代码,在IOS 13.3下执行时,首次必返回false. if (confirm("是否继续")) {alert("继续"); } else {al ...

  8. PacketGetAdapterNames返回false

    今天在写一个关于ARP程序的时候,发现PacketGetAdapterNames函数,获取所有网卡适配器名称,既然返回false,非常奇怪. 这个函数就是通过读取注册表的方面,读取网卡的名称,怎么会失 ...

  9. Android Context.bindService 返回 false 问题

    前段时间在项目中遇到一个问题,使用bindService绑定服务竟然返回false. 绑定失败,自然也就不会调用ServiceConnection的onServiceConnected方法. 对于bi ...

最新文章

  1. 虚拟机下判断文件或目录是否存在
  2. maven编译java1.8项目_maven正在用java 1.7编译代码,但我想用1.8编译它
  3. flink报错:Error: Static methods in interface require -target:jvm-1.8 已解决
  4. android intent传递数据
  5. 【整理】强化学习与MDP
  6. dedeCMS解决问题:“用户资料尚未通过审核,因此空间禁止访问”?
  7. 库克:10年内可能离开苹果
  8. mysql 分区表 限制_Mysql分区表使用的一些限制和需要注意的地方-阿里云开发者社区...
  9. win10官方原版镜像系统
  10. js判断IE内核,IE浏览器版本
  11. 无穷积分 ∫sinx/xdx 的几种巧妙解法
  12. 《如何成为一个会学习的人》
  13. java pdf 中文字体_iText生成pdf中文字体解决方案
  14. 射频芯片设计EM仿真(二)--对比EM仿真和schmetic仿真
  15. cad修改快捷键_人手一份的绘图命令,CAD绘图必备,学会工资上万不是梦
  16. r语言c函数怎么用,R语言学习笔记——C#中如何使用R语言setwd()函数
  17. 遥感基础之全色波段和多光谱
  18. STM32物联网通讯GPRS
  19. 单片机C语言视频教程转让
  20. Linux deepin 安装mysql

热门文章

  1. [html] 如何根据设备尺寸做页面自适应?
  2. 前端学习(2848):鼠标点击事件
  3. 工作287:命名报错
  4. 前端学习(2713):重读vue电商网站33之实现首页路由重定向
  5. 前端学习(2561):页面更新
  6. “约见”面试官系列之常见面试题之第七十篇之==和===(建议收藏)
  7. 前端学习(713)创建数组
  8. 第十六期:简单的介绍一下大数据中最重要的MapReduce
  9. 实例19:python
  10. openssl创建CA并签发证书