FutureTask isDone 返回 false

大家好,我是烤鸭:

​     今天看一下 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);}
}

属实是有点水了…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/412540.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

为什么MySQL数据库要用B+树存储索引

A&#xff1a;为什么MySQL数据库要用B树存储索引&#xff1f; Hash的查找速度为O(1)&#xff0c;而树的查找速度为O(log2n)&#xff0c;为什么不用Hash作为数据库的存储索引呢&#xff1f; 树的话&#xff0c;无非就是前中后序遍历、二叉树、二叉搜索树、平衡二叉树&#xff0c…

[css] rgba()和opacity这两个的透明效果有什么区别呢?

[css] rgba()和opacity这两个的透明效果有什么区别呢&#xff1f; 1.opacity 是属性&#xff0c;rgba()是函数&#xff0c;计算之后是个属性值&#xff1b; 2.opacity 作用于元素和元素的内容&#xff0c;内容会继承元素的透明度&#xff0c;取值0-1&#xff1b; 3.rgba() 一般…

lettuce 配置域名 dns 切换

大家好&#xff0c;我是烤鸭&#xff1a; 如果你也有类似的困扰&#xff0c;运维告诉你&#xff0c;redis连接配置域名&#xff0c;这样出问题了&#xff0c;直接改dns地址就行&#xff0c;不需要重启服务。。。梦想是美好的&#xff0c;现实是残酷的。如果你使用的是 let…

[css] 怎样修改chrome记住密码后自动填充表单的黄色背景?

[css] 怎样修改chrome记住密码后自动填充表单的黄色背景&#xff1f; input:-webkit-autofill { -webkit-box-shadow: 0 0 3px 100px #eee inset; //改变填充背景色 }个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢…

[css] 说说你对z-index的理解

[css] 说说你对z-index的理解 层叠 就是Z轴的方向的位置&#xff0c;值越大离屏幕前的你越近&#xff0c;反之亦然。可以为负数个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通…

zuul 1.x 和gateway性能对比

大家好&#xff0c;我是烤鸭&#xff1a; 今天分享下 zuul和gateway 网关压测。 环境&#xff1a; windows 10 jdk 8 压测工具&#xff1a; wrk jmeter 数据对比 场景是仅单独转发&#xff0c;接口 Thread.sleep(50) jmeter 12 线程&#xff0c;30s zuul&#xf…

[css] 在页面中的应该使用奇数还是偶数的字体?为什么呢?

[css] 在页面中的应该使用奇数还是偶数的字体&#xff1f;为什么呢&#xff1f; 常用偶数号字体,但奇数号字体也没关系,例如 知乎正文使用15px字体,豆瓣电影使用13px字体UI设计师导出的设计稿一般都是偶数号字体偶数字号容易和页面其他标签的其他属性形成比例关系Windows 自带的…

redisson 大量ping操作,导致 tps过高

大家好&#xff0c;我是烤鸭&#xff1a; 这个问题有点奇怪&#xff0c;新服务上线&#xff0c;redis tps居高不下&#xff0c;还都是ping命令。 环境&#xff1a; 服务 &#xff1a; 280台&#xff0c;redis集群&#xff1a;12主24从 问题 由于服务刚上线&#xff0c;还没…

关于-编码进阶

中国电脑的windows 的默认编码是gbk. "记事本"是gbk, 和windows的编码一样.linux 的默认编码是utf-8苹果OS的默认编码是utf-8.str 字符串在内部的默认编码是 unicode # 英文str: 表现形式alex内部编码: 字符串str-> unicodebytes:表新形式 balex      …

PMP 学习总结

大家好&#xff0c;我是烤鸭&#xff1a; PMP终于考过了。成绩出了一个月了&#xff0c;一直想写一篇总结但没下笔&#xff0c;主要原因最近有点忙(太懒了)。考试的内容是基于第6版的。 晒个证书 证书上没写等级&#xff0c;一般都宣称5A过(其实我是 4A1T过的)。 学习过程…

[css] 你有用过CSS预处理器吗?喜欢用哪个?原理是什么?

[css] 你有用过CSS预处理器吗&#xff1f;喜欢用哪个&#xff1f;原理是什么&#xff1f; 它能让你的CSS具备更加简洁、适应性更强、可读性更强、层级关系更加明显、更易于代码的维护等诸多好处。 CSS预处理器种类繁多&#xff0c;目前Sass、Less、用的比较多。 使用功能&…

处理器映射器(HandlerMapping)及处理器适配器(HandlerAdapter)详解(一)

非注解 处理器映射器 和 处理器适配器 处理器映射器&#xff1a; 第一种: BeanNameUrlHandlerMapping <!-- 配置Handler --> <bean id"userController1" name"/queryUsers.action" class"com.bjxb.ssm.controller.UserController" />…

Gateway Sentinel 做网关降级/流控,转发header和cookie

大家好&#xff0c;我是烤鸭&#xff1a; Springcloud Gateway 使用 Sentinel 流量控制。 环境 springcloud-gateway的网关应用&#xff0c;springboot的服务&#xff0c;nacos作为注册中心 sentinel-dashboard-1.8.2 最新版下载地址&#xff1a; https://github.com/aliba…

[css] 说说CSS的优先级是如何计算的?

[css] 说说CSS的优先级是如何计算的&#xff1f; 选择器种类严格来讲&#xff0c;选择器的种类可以分为三种&#xff1a;标签名选择器、类选择器和ID选择器。而所谓的后代选择器和群组选择器只不过是对前三种选择器的扩展应用。而 在标签内写入 style"" 的方式&…

django后台数据管理admin设置代码

新建admin用户 createsuperuser 设定好用户名&#xff0c;邮箱&#xff0c;密码 设置setting LANGUAGE_CODE zh-hansTIME_ZONE Asia/ShanghaiUSE_I18N TrueUSE_L10N TrueUSE_TZ False 在写好的users的app下修改admin.py # -*- coding: utf-8 -*- from __future__ import u…

rocketmq 初探(一)

大家好&#xff0c;我是烤鸭&#xff1a; 今天看下rocketmq。这篇主要是简单介绍下 rocketmq以及idea 本地调试 rocketmq。 项目架构 感兴趣的可以下载源码看下。 https://github.com/apache/rocketmq 项目结构图。 rocketmq-acl: acl 秘钥方式的鉴权&#xff0c;用在bro…

[css] 说说浏览器解析CSS选择器的过程?

[css] 说说浏览器解析CSS选择器的过程&#xff1f; 按照从上到下&#xff0c;从右到左的顺序解析。个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题

客户将数据库迁移上云的常用办法

下载网站:www.SyncNavigator.CN 客服QQ1793040---------------------------------------------------------- 关于HKROnline SyncNavigator 注册机价格的问题 HKROnline SyncNavigator 8.4.1 非破解版 注册机 授权激活教程 最近一直在研究数据库同步的问题&#xff0c;在网上…

基于nchan打造百万用户的聊天室

大家好&#xff0c;我是烤鸭&#xff1a; 这次介绍下nchan&#xff0c;nginx的一个module。 nchan 源码: https://github.com/slact/nchan 官网: https://nchan.io/ nginx 配置说明文档: https://nchan.io/documents/nginxconf2016-slides.pdf 测试环境搭建 4 台linux cent…

springboot 获取控制器参数的几种方式

这里介绍springboot 获取控制器参数有四种方式 1、无注解下获取参数 2、使用RequestParam获取参数 3、传递数组 4、通过URL传递参数 无注解下获取参数无注解下获取参数&#xff0c;需要控制器层参数与HTTP请求栏参数名称一致&#xff0c;这样后台就可以获取到请求栏的参数。 /*…