源码解读之FutureTask如何实现最大等待时间
预备知识:Java 线程挂起的常用方式有以下几种
Thread.sleep(long millis):这个方法可以让线程挂起一段时间,并释放 CPU 时间片,等待一段时间后自动恢复执行。这种方式可以用来实现简单的定时器功能,但如果不恰当使用会影响系统性能。Object.wait()和Object.notify()或Object.notifyAll():这是一种通过等待某个条件的发生来挂起线程的方式。wait()方法会让线程等待,直到其他线程调用了notify()或notifyAll()方法来通知它。这种方式需要使用 synchronized 或者 ReentrantLock 等同步机制来保证线程之间的协作和通信。LockSupport.park()和LockSupport.unpark(Thread thread):这两个方法可以让线程挂起和恢复。park()方法会使当前线程挂起,直到其他线程调用了unpark(Thread thread)方法来唤醒它。这种方式比较灵活,可以根据需要控制线程的挂起和恢复。
一:示例-引入主题
public class FutureTaskDemo {
public static void main(String[] args) {
FutureTask<String> futureTask = new FutureTask<>(new Callable() {
@Override
public 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 yet
Thread.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);
}
else
LockSupport.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 state
finishCompletion();
//轨迹变化 2.NEW -> COMPLETING -> EXCEPTIONAL (异常)
}
//否则1: 3.NEW -> CANCELLED (任务被取消)
//否则2: 4.NEW -> INTERRUPTING -> INTERRUPTED(任务被打断)
}
正常轨迹 set(result);
1.NEW -> COMPLETING -> NORMAL (成功完成)
源码解读之FutureTask如何实现最大等待时间的更多相关文章
- Future、FutureTask实现原理浅析(源码解读)
前言 最近一直在看JUC下面的一些东西,发现很多东西都是以前用过,但是真是到原理层面自己还是很欠缺. 刚好趁这段时间不太忙,回来了便一点点学习总结. 前言 最近一直在看JUC下面的一些东西,发现很多东 ...
- ScheduledThreadPoolExecutor源码解读
1. 背景 在之前的博文--ThreadPoolExecutor源码解读已经对ThreadPoolExecutor的实现原理与源码进行了分析.ScheduledExecutorService也是我们在 ...
- ThreadPoolExecutor源码解读
1. 背景与简介 在Java中异步任务的处理,我们通常会使用Executor框架,而ThreadPoolExecutor是JUC为我们提供的线程池实现. 线程池的优点在于规避线程的频繁创建,对线程资源 ...
- SDWebImage源码解读之SDWebImageDownloaderOperation
第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...
- SDWebImage源码解读 之 NSData+ImageContentType
第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...
- SDWebImage源码解读 之 UIImage+GIF
第二篇 前言 本篇是和GIF相关的一个UIImage的分类.主要提供了三个方法: + (UIImage *)sd_animatedGIFNamed:(NSString *)name ----- 根据名 ...
- SDWebImage源码解读 之 SDWebImageCompat
第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...
- SDWebImage源码解读_之SDWebImageDecoder
第四篇 前言 首先,我们要弄明白一个问题? 为什么要对UIImage进行解码呢?难道不能直接使用吗? 其实不解码也是可以使用的,假如说我们通过imageNamed:来加载image,系统默认会在主线程 ...
- SDWebImage源码解读之SDWebImageCache(上)
第五篇 前言 本篇主要讲解图片缓存类的知识,虽然只涉及了图片方面的缓存的设计,但思想同样适用于别的方面的设计.在架构上来说,缓存算是存储设计的一部分.我们把各种不同的存储内容按照功能进行切割后,图片缓 ...
- SDWebImage源码解读之SDWebImageCache(下)
第六篇 前言 我们在SDWebImageCache(上)中了解了这个缓存类大概的功能是什么?那么接下来就要看看这些功能是如何实现的? 再次强调,不管是图片的缓存还是其他各种不同形式的缓存,在原理上都极 ...
随机推荐
- vmware workstation 16.x安装windows server 2012 R2无法点击安装vmware tools选项
在使用vmware workstation预设安装windows server 2012 R2后,提示需要先升级KBxxxxx更新才可以安装vmware tools,但是在安装了所需的更新后,发现菜单 ...
- JAVA第四五次大作业及期中考试总结
21201411-李英涵 继承与多态blog2 前言: 期中考试的题目还可以,因为给了类图,但是难度也逐渐升高. pta中的四的第一题和第三题较为简单,因为第一题的正则 ...
- python字符串拼接方式
一 .join(iterator),后面必须是可迭代对象,例如:字符串,列表,元组 testList = ["1","2"] print(",&quo ...
- django_url反向解析
**************************************************************************************************** ...
- 深入理解Java内存(图解)
这篇文章是转自http://blog.csdn.net/shimiso/article/details/8595564博文. 本文分析基于jdk1.8 进入正题前首先要知道的是Java程序运行在JVM ...
- logic 运算符
- Sqoop连接数据库MySQL报错
1.问题描述 (1)问题示例: [Hadoop@master TestDir]$ sqoop list-databases --connect jdbc:mysql://master:3306/ -- ...
- 泛型 ? extends E ? super E的区别
package cn.itcast.generic; import java.util.ArrayList;import java.util.Collection; public class Supp ...
- 西电oj245 成绩统计 结构体数组使用
#include<stdio.h> struct student{ //定义一个结构体数组 int num; char name[11]; float g1; float g2; floa ...
- 手写深度比较isEqual函数
function isObject(obj){ return typeof obj ==='object'&&obj!==null } functon isEqual(obj1,obj ...