【杂谈】从实现角度看ChannelFuture
JDK中的Future特性
在介绍Netty的ChannelFuture之前,我们先来看看JDK中的Future是如何实现的。总的来说就是任务提交的时候会使用装饰器模式,将任务包装成一个FutureTask。当执行器执行该Task的时候,不仅仅会执行用户提交的任务,还会执行装饰器添加的额外操作,例如在执行之前记录当前执行线程、执行完成后将任务结果保存在FutureTask对象内部等。
- Thread runner => 装饰器添加的,在执行任务之前,会在对象内保存当前执行线程的引用,用于中断任务执行
- Object outcome => 任务执行结果(返回值或异常对象),任务执行完成后会将结果set到此对象的outcome,后续可通过Future的get接口取出
- Callable<V> callble => 用户提交的实际任务
- WaitNode waiters => 用于保存等待线程,任务完成后会唤醒这些线程
详细请看本人过去整理的随笔:
Netty中的ChannelFuture
ChannelFuture是在Future基础上的完善,它支持添加监听器,在任务完成后自动执行相关操作。
这个其实就是观察者模式,个人认为就是在前面的C部分添加监听的方法调用,即执行线程执行完成后,如果发现该任务上有监听器,则该执行线程还会调用监听器接口。
话不多说,我们来看看它代码到底是怎么写的,跟Future的实现有何异同。相关实现关键内容在DefaultPromise类中,相对于前面的FutureTask。
1.对象属性
private volatile Object result;
private final EventExecutor executor;
private Object listeners;
private short waiters;
private boolean notifyingListeners;
从对象属性中我们可以得知,该对象没有保存用户任务。实际上个人任务这是它跟Future最大的不同,Future所引用的本质上是一个任务,而ChannelFuture所引用的只有任务状态和任务结果。所以这个DefaultPromise对象不会被作为任务提交到执行器中。
2.任务完成时的线程唤醒与监听器触发
private boolean setValue0(Object objResult) {
if (RESULT_UPDATER.compareAndSet(this, null, objResult) ||
RESULT_UPDATER.compareAndSet(this, UNCANCELLABLE, objResult)) {
if (checkNotifyWaiters()) {
notifyListeners();
}
return true;
}
return false;
}
当任务执行完成,通过setValue将值传入DefaultPromise对象时,唤醒等待的线程,并触发监听器。
2.线程的等待与唤醒方式
public Promise<V> await() throws InterruptedException {
if (isDone()) {
return this;
} if (Thread.interrupted()) {
throw new InterruptedException(toString());
} checkDeadLock(); synchronized (this) {
while (!isDone()) {
incWaiters();
try {
wait();
} finally {
decWaiters();
}
}
}
return this;
}
private synchronized boolean checkNotifyWaiters() {
if (waiters > 0) {
notifyAll();
}
return listeners != null;
}
从上述代码可知,这里的线程等待与唤醒方式使用的内置的wait()和notify()方法,而FutureTask的等待队列是单独实现的。
注:这里尚不清楚这两种实现方式之间的优劣。
3.设置监听器
public Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) {
checkNotNull(listener, "listener"); synchronized (this) {
addListener0(listener);
} //添加监听器时,如果发现已经完成,则直接调用触发监听器
if (isDone()) {
notifyListeners();
} return this;
}
如果任务已经完成,则直接触发监听器。防止出现"调用setLisener的时候,任务已经完成,导致监听器不被触发"。
4.任务取消
public boolean cancel(boolean mayInterruptIfRunning) {
if (RESULT_UPDATER.compareAndSet(this, null, CANCELLATION_CAUSE_HOLDER)) {
if (checkNotifyWaiters()) {
notifyListeners();
}
return true;
}
return false;
}
从代码可以看出,它的实现与FutureTask有所不同,它并不会尝试调用执行线程的interrupt()方法来中断线程,只是将等待线程唤醒,并触发监听器。所以这个“取消”操作并不会影响代码的实际执行。
事实上“中断”也只是一种协作方式,它只是设置中断状态并将线程唤醒(如果该线程正处于挂起状态),如果用户代码中没有对中断状态进行判断,也没有使用wait()、sleep()等方法,中断操作也是不会实际“打断”代码执行的。
详细可看:【杂谈】线程中断——Interrupt
【杂谈】从实现角度看ChannelFuture的更多相关文章
- Android IOS WebRTC 音视频开发总结(四八)-- 从商业和技术的角度看视频行业的机会
本文主要从不同角度介绍视频行业的机会,文章来自博客园RTC.Blacker,支持原创,转载必须说明出处,欢迎关注个人微信公众号blacker ----------------------------- ...
- 【阿里云产品公测】以开发者角度看ACE服务『ACE应用构建指南』
作者:阿里云用户mr_wid ,z)NKt# @I6A9do 如果感觉该评测对您有所帮助, 欢迎投票给本文: UO<claV RsfTUb)< 投票标题: 28.[阿里云 ...
- [置顶] 从引爆点的角度看360随身wifi的发展
从引爆点的角度看360随身wifi的发展 不到一个月的时间,随身wifi预定量就数百万.它的引爆点在哪里,为什么相同的产品这么多它却能火起来,通过对随身wifi的了解和我知识层面分析,主要是因为随身w ...
- 站在Java的角度看LinkedList
站在Java的角度看,玩队列不就是玩对象引用对象嘛! public class LinkedList<E> implements List<E>, Deque<E> ...
- 从源码的角度看 React JS 中批量更新 State 的策略(下)
这篇文章我们继续从源码的角度学习 React JS 中的批量更新 State 的策略,供我们继续深入学习研究 React 之用. 前置文章列表 深入理解 React JS 中的 setState 从源 ...
- 从线程模型的角度看Netty的高性能
转载:Netty(二) 从线程模型的角度看 Netty 为什么是高性能的? 传统 IO 在 Netty 以及 NIO 出现之前,我们写 IO 应用其实用的都是用 java.io.* 下所提供的包. 比 ...
- INDEX--从数据存放的角度看索引2
在上次<INDEX--从数据存放的角度看索引>中,我们说到"唯一非聚集索引"和“非唯一非聚集索引”在存储上有一个明显的差别:唯一非聚集索引的非叶子节点上不会包含RID的 ...
- 从JDK源码角度看Short
概况 Java的Short类主要的作用就是对基本类型short进行封装,提供了一些处理short类型的方法,比如short到String类型的转换方法或String类型到short类型的转换方法,当然 ...
- 从JDK源码角度看Byte
Java的Byte类主要的作用就是对基本类型byte进行封装,提供了一些处理byte类型的方法,比如byte到String类型的转换方法或String类型到byte类型的转换方法,当然也包含与其他类型 ...
随机推荐
- python:用cv2简单实现图片的水平、垂直翻转
原图片的本地地址:D:/360Downloads/test.jpg 代码实现: # 导入cv2模块 import cv2 # 给出本地图片的地址 img_dir="D:/360Downloa ...
- Pointer Lock API(2/3):属性、方法、事件
Pointer Lock API 提供了三个属性.两个方法.两个事件 Tabel Of Content 属性 Document.pointerLockElement Document.onpointe ...
- CentOS 7 Docker安装
1. uname -a 查询机器信息,确保CPU为64位,且Linux内核在3.10版本以上 2. 更新yum包: yum update 3. 在 /etc/yum.repos.d下创建 docker ...
- (js描述的)数据结构[哈希表1.2](9)
一. 优秀的哈希函数 1.快速的计算: 需要快速的计算来获得对应的hashCode(霍纳法则来减少乘除次数) 2.均匀的分布: 尽可能将元素映射到不同的位置,让元素在哈希表中均匀分布 二.哈希表的扩容 ...
- Apache Hudi 设计与架构最强解读
感谢 Apache Hudi contributor:王祥虎 翻译&供稿. 欢迎关注微信公众号:ApacheHudi 本文将介绍Apache Hudi的基本概念.设计以及总体基础架构. 1.简 ...
- python3(三十一)metaclass
""" """ __author__ = 'shaozhiqi' # 动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而 ...
- Python 操作mysql数据库之 SQLAlchemy 案例详解
前言: 字段声明类型中,最右边的是数据库中对应的字段,我们依然可以使用,其左边的的 SQLAchemy 则是其自身封装的自定义类型. 本篇不会讲太多的理论知识,因为这个实用性更强,所以通篇全部都是 ...
- 使用Network Emulator Toolkit工具模拟网络丢包测试(下)
用户会在各种网络环境下使用我们的App,PC应用,我们决不能祈求用户的网络环境都是稳定的,因此我们需要模拟出弱网络的情况,用来测试我们的APP在弱网络环境下的表现如何.Network Emulator ...
- 怎么快速学python?酒店女服务员一周内学会Python,一年后成为程序员
怎么快速学python?有人说,太难!但这个女生却在一个星期内入门Python,一个月掌握python所有的基础知识点. 说出来你应该不信,刚大学毕业的女生:琳,一边在酒店打工,一边自学python, ...
- 关于gpu版本的tensorflow+anaconda+jupyter的一些安装问题(持续更新)
关于anaconda安装,虽然清华镜像站资源很丰富,但是不知道是网络还是运气的问题,用这个路径安装的时候总是出现文件丢失.具体表现可能是anaconda prompt 找不到,conda命令无效等问题 ...