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的更多相关文章

  1. Android IOS WebRTC 音视频开发总结(四八)-- 从商业和技术的角度看视频行业的机会

    本文主要从不同角度介绍视频行业的机会,文章来自博客园RTC.Blacker,支持原创,转载必须说明出处,欢迎关注个人微信公众号blacker ----------------------------- ...

  2. 【阿里云产品公测】以开发者角度看ACE服务『ACE应用构建指南』

    作者:阿里云用户mr_wid ,z)NKt#   @I6A9do   如果感觉该评测对您有所帮助, 欢迎投票给本文: UO<claV   RsfTUb)<   投票标题:  28.[阿里云 ...

  3. [置顶] 从引爆点的角度看360随身wifi的发展

    从引爆点的角度看360随身wifi的发展 不到一个月的时间,随身wifi预定量就数百万.它的引爆点在哪里,为什么相同的产品这么多它却能火起来,通过对随身wifi的了解和我知识层面分析,主要是因为随身w ...

  4. 站在Java的角度看LinkedList

    站在Java的角度看,玩队列不就是玩对象引用对象嘛! public class LinkedList<E> implements List<E>, Deque<E> ...

  5. 从源码的角度看 React JS 中批量更新 State 的策略(下)

    这篇文章我们继续从源码的角度学习 React JS 中的批量更新 State 的策略,供我们继续深入学习研究 React 之用. 前置文章列表 深入理解 React JS 中的 setState 从源 ...

  6. 从线程模型的角度看Netty的高性能

    转载:Netty(二) 从线程模型的角度看 Netty 为什么是高性能的? 传统 IO 在 Netty 以及 NIO 出现之前,我们写 IO 应用其实用的都是用 java.io.* 下所提供的包. 比 ...

  7. INDEX--从数据存放的角度看索引2

    在上次<INDEX--从数据存放的角度看索引>中,我们说到"唯一非聚集索引"和“非唯一非聚集索引”在存储上有一个明显的差别:唯一非聚集索引的非叶子节点上不会包含RID的 ...

  8. 从JDK源码角度看Short

    概况 Java的Short类主要的作用就是对基本类型short进行封装,提供了一些处理short类型的方法,比如short到String类型的转换方法或String类型到short类型的转换方法,当然 ...

  9. 从JDK源码角度看Byte

    Java的Byte类主要的作用就是对基本类型byte进行封装,提供了一些处理byte类型的方法,比如byte到String类型的转换方法或String类型到byte类型的转换方法,当然也包含与其他类型 ...

随机推荐

  1. 关于C#三层架构增删改查中的“添加”问题

    关于“添加”功能的实现比较简单: 先来一个简单的界面: 然后是代码: ··采用的是三层架构的思想写的·· 在DAO中的方法为: (使用了动软自动生成代码) 希望对您有所帮助!

  2. mac 升级到mavericks 安装php扩展现问题

    mac 升级到mavericks 安装php扩展现以下问题 grep: /usr/include/php/main/php.h: No such file or directory grep: /us ...

  3. Mac osx下误删了mach_kernel文件,如何找回

    brew install xar 假设当前有一个 pkg 文件"filename.pkg",先使用以下命令解开 pkg: $ xar -xf filename.pkg 解压后发现其 ...

  4. java中查询某个类已经创建了多少个对象了

    这个代码主要是使用类的静态字段和构造函数,可以跟踪某个类所创建对象的个数.请写一个类,在任何时候都可以向它查询“你已经创建了多少个对象? 主要是在构造函数中用到了静态数据,进行显示已经构造了多少个类对 ...

  5. Android视频悬浮窗口实现

    前言 本文例子实现了点击显示悬浮窗口,同时窗口可播放视频,拖动位置,点击关闭及返回APP页面,通过例子来讲述悬浮窗口实现原理及细节处理,效果图如下所示: 原理 WindowManager对View视图 ...

  6. 31 Exception 异常处理

    /* * Exception in thread "main" java.lang.ArithmeticException: / by zero at com.itheima_01 ...

  7. 2020-3-15 20175110王礼博 Exp2后门原理与实践

    目录 1.使用netcat获取主机操作Shell,cron启动 2.使用socat获取主机操作Shell, 任务计划启动 3.使用MSF meterpreter(或其他软件)生成可执行文件,利用nca ...

  8. coding++:都说新的Arraylist 扩容是(1.5倍+1) 看了1.8的源代码发现不是这么回事

     都说新的Arraylist 扩容是(1.5倍+1) 看了1.8的源代码发现不是这么回事 就用下面这段代码在jdk的三个版本运行看了下效果: import java.lang.reflect.Fiel ...

  9. Java队列学习第一篇之列介绍

    Java并发之显式锁和隐式锁的区别 在面试的过程中有可能会问到:在Java并发编程中,锁有两种实现:使用隐式锁和使用显示锁分别是什么?两者的区别是什么?所谓的显式锁和隐式锁的区别也就是说说Synchr ...

  10. .Net Core MVC 基于Cookie进行用户认证

    在打代码之前先说一下思路. 登录的的时候服务端生成加密的字符串(用户名.id.当前时间)并且存入客户端cookie中,服务端的缓存中.对客户端的每次请求进行拦截,解密保存在cookie中的加密字符串. ...