源码解读之FutureTask如何实现最大等待时间

预备知识:Java 线程挂起的常用方式有以下几种

  1. Thread.sleep(long millis):这个方法可以让线程挂起一段时间,并释放 CPU 时间片,等待一段时间后自动恢复执行。这种方式可以用来实现简单的定时器功能,但如果不恰当使用会影响系统性能。
  2. Object.wait() 和 Object.notify() 或 Object.notifyAll():这是一种通过等待某个条件的发生来挂起线程的方式。wait() 方法会让线程等待,直到其他线程调用了 notify() 或 notifyAll() 方法来通知它。这种方式需要使用 synchronized 或者 ReentrantLock 等同步机制来保证线程之间的协作和通信。
  3. LockSupport.park() 和 LockSupport.unpark(Thread thread):这两个方法可以让线程挂起和恢复。park() 方法会使当前线程挂起,直到其他线程调用了 unpark(Thread thread) 方法来唤醒它。这种方式比较灵活,可以根据需要控制线程的挂起和恢复。

先上结论:

 

1.futureTask.get时通过LockSupport.park()挂起线程

2.在Thread.run() 方法中 调用 setException(ex)或set(result),然后调用LockSupport.unpark(t)唤醒线程。

一:示例-引入主题

public class FutureTaskDemo {public static void main(String[] args) {FutureTask<String> futureTask = new FutureTask<>(new Callable() {@Overridepublic Object call() throws Exception {System.out.println("异步线程执行");Thread.sleep(3000);//模拟线程执行任务需要3秒return "ok";}});Thread t1 = new Thread(futureTask, "线程一");t1.start();try {//关键代码String s = futureTask.get(2, TimeUnit.SECONDS); //最大等待线程2秒} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();} catch (TimeoutException e) {e.printStackTrace();}}
}

二:进入futureTask.get(2, TimeUnit.SECONDS);

  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) //重点awaitDone,即完成了最大等待,依然没有结果就抛出异常逻辑throw new TimeoutException();return report(s);}

​ awaitDone返回线程任务执行状态,即小于等于COMPLETING(任务正在运行,等待完成)抛出异常TimeoutException

三:进入(awaitDone(true, unit.toNanos(timeout)))原理分析

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

3.1 总体解读awaitDone

利用自旋(for (;😉)的方式 ,检查state(任务状态)与waitNode(维护等待的线程),

第一步:首先检查if (Thread.interrupted()) 线程是否被打断(LockSupport.parkNanos挂起的线程被打断不抛出异常),

第二步:判断任务状态与waitNode是否入队+确定最大等待时间

​ 若已完成(if (s > COMPLETING))返回任务状态

​ 若已完成(if (s == COMPLETING))-->表示正在完成,但尚未完成。则让出 CPU,进入就绪状态,等待其他线程的执行

​ 若if (q == null)==>创建等待等待节点

​ 若if (!queued)==>表示上一步创建的节点没有和当前线程绑定,故绑定

​ 最后else if (timed)与else,判断最大等待时间

static final class WaitNode {volatile Thread thread;volatile WaitNode next;WaitNode() { thread = Thread.currentThread(); }}
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;
state可能转换的过程 1.NEW -> COMPLETING -> NORMAL (成功完成)2.NEW -> COMPLETING -> EXCEPTIONAL (异常)3.NEW -> CANCELLED (任务被取消)4.NEW -> INTERRUPTING -> INTERRUPTED(任务被打断)

3.2 关键代码

LockSupport.park(this, nanos) ==内部实现==> UNSAFE.park(false, nanos)();

​ 即让当前线程堵塞直至指定的时间(nanos),该方法同Thread.sleep()一样不会释放持有的对象锁,但不同的是Thread.sleep会被打断(interrupted)并抛出异常,而LockSupport.park被打断不会抛出异常,故在自旋时(for (;😉)需判断if (Thread.interrupted())线程是否被打断(手动抛出异常)。

四:线程运行时state的变化轨迹

4.1:新建时利用构造器设置state=NEW

 public FutureTask(Runnable runnable, V result) {this.callable = Executors.callable(runnable, result);this.state = NEW;   // 赋值状态}

4.2: 线程运行时state可能变化轨迹

public void run() {..........防止多次运行stat()方法..............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 {runner = null;//----最后结束---防止线程被打断int s = state;if (s >= INTERRUPTING)handlePossibleCancellationInterrupt(s);}}

异常轨迹setException(ex)

protected void setException(Throwable t) {if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {outcome = t;UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final statefinishCompletion();//轨迹变化 2.NEW -> COMPLETING -> EXCEPTIONAL (异常)}//否则1: 3.NEW -> CANCELLED (任务被取消)//否则2: 4.NEW -> INTERRUPTING -> INTERRUPTED(任务被打断)
}

正常轨迹 set(result);

 1.NEW -> COMPLETING -> NORMAL (成功完成)

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

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

相关文章

vue文件上传,文件打不开。文件上传 on-progress不触发

//文件上传带进度条<el-uploadv-model:file-list"fileList":limit"3":on-progress"beforeAvatarUpload"//on-progress 不触发&#xff0c;触发的关键在于覆盖原有的http请求:http-request"getFile":action"${app.api}/student…

投个 3D 冰壶,上班玩一玩 | 物理引擎

本篇文章将介绍如何使用物理引擎和图扑 3D 可视化技术来呈现冰壶运动的模拟。 Oimo.js 物理引擎 Oimo.js 是一个轻量级的物理引擎&#xff0c;它使用 JavaScript 语言编写&#xff0c;并且基于 OimoPhysics 引擎进行了改进和优化。Oimo.js 核心库只有 150K &#xff0c;专门用…

【原创】实现GPT中Transformer模型之框架概念

作者&#xff1a;黑夜路人 时间&#xff1a;2023年7月 GPT是什么意思 GPT的全称是 Generative Pre-trained Transformer&#xff08;生成型预训练变换模型&#xff09;&#xff0c;它是基于大量语料数据上训练&#xff0c;以生成类似于人类自然语言的文本。其名称中的“预训练”…

linux远程服务器和本地服务器相互之间传输文件方法SSH(乌班图系统)

提前知道几点&#xff1a; 1.使用的MobaXterm软件 2.访问本地、远程服务器&#xff0c;需要账号和密码&#xff08;远程机器上的用户名和密码 远程机器的 IP 地址或主机名&#xff08;在同一子网上&#xff09;&#xff09;。每次访问需要输入密码&#xff0c;可以搜Ubuntu SS…

8、gateway使用和原理

一、什么是Spring Cloud Gateway 1、网关简介 网关作为流量的入口&#xff0c;常用的功能包括路由转发&#xff0c;权限校验&#xff0c;限流等。 2、Gateway简介 Spring Cloud Gateway 是Spring Cloud官方推出的第二代网关框架&#xff0c;定位于取代 Netflix Zuul。相比 …

QT禁用窗口【关闭】按钮的实现方法

QT禁用窗口关闭按钮的实现方法&#xff0c;直接在窗体类构造函数的内部写入setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint)即可实现&#xff0c;案例如下&#xff1a; #include "form.h" #include "…

Nginx Linux设置开机自启动

使用如下命令 vi /lib/systemd/system/nginx.service 创建并编辑文件将以下代码黏贴至此文件中 [Unit] Descriptionnginx Afternetwork.target[Service] Typeforking TimeoutSec0 #防止启动超时 Userroot Grouproot criptionnacos Afternetwork.target[Service] Typeforking T…

Visual Studio 自定义的颜色字体不生效

问题描述&#xff1a; 1、dll1中引用第三方库的类不识别&#xff0c;颜色黑白&#xff0c;自定义颜色不生效&#xff1b;定义的是结构体 2、在dll2引用另一个dll1中的结构体。结构体不识别&#xff0c;今天成员函数cpp中自定义颜色不生效。 问题解决方式&#xff1a; 全部清…

业务变革与架构双驱动的多项目管理︱海康威视流程变革咨询顾问张燕飞

海康威视数字技术股份有限公司流程与变革管理部流程变革咨询顾问张燕飞女士受邀为由PMO评论主办的2023第十二届中国PMO大会演讲嘉宾&#xff0c;演讲议题&#xff1a;业务变革与架构双驱动的多项目管理。大会将于8月12-13日在北京举办&#xff0c;敬请关注&#xff01; 议题简要…

[Volo.Abp升级笔记]使用旧版Api规则替换RESTful Api以兼容老程序

Volo.Abp 配置应用层自动生成Controller&#xff0c;增删查改服务&#xff08;CrudAppService&#xff09;将会以RESTful Api的方式生成对应的接口 (官方文档)&#xff0c;这与旧版本的Abp区别很大。RESTful固然好&#xff0c;虽然项目里新的Api会逐步使用RESTful Api代替旧的&…

STM32单片机OLED智能饮水机童锁自动出水补水加热水位检测

实践制作DIY- GC0159--OLED智能饮水机 基于STM32单片机设计---OLED智能饮水机 二、功能介绍&#xff1a; 电路组成&#xff1a;STM32F103CXT6最小系统 OLED显示器DS18B20检测水温1个继电器模拟加热1个继电器模拟补水1个继电器模拟出水水位传感器超声波测距多个按键&#xff08…

81. 正则表达式

一、概述二、匹配单个字符三、匹配一组字符四、使用元字符五、重复匹配六、位置匹配七、使用子表达式八、回溯引用九、前后查找十、嵌入条件参考资料 一、概述 正则表达式用于文本内容的查找和替换。 正则表达式内置于其它语言或者软件产品中&#xff0c;它本身不是一种语言或…