IoFuture是和IoSession紧密相连的一个类,在官网上并没有对它的描述,因为它一般不会显示的拿出来用,权当是一个工具类被session所使用。当然在作用上,这个系列可并不简单,我们先看源码的注释对它的描述:

IoFuture represents the completion of an asynchronous I/O operation on an IoSession.

这个类是提供异步操作的一个工具,所以在读源码之前,必须对异步IO操作有所了解,然后我们才可以顺着这条路往下走。关于异步IO的介绍可以看:《同步、异步、阻塞、非阻塞》

IoFuture通过IoFutureListener被IoSession绑定,它的实现都放在org.apache.mina.core.future下。在IoFuture的实现中,分别提供了读、写、连接、关闭的future,通过这四个future来实现异步操作。异步操作很重要的一部分就是对线程的控制,所以在IoFuture这个接口中,我们能很清楚的理清这几个方法:await、join。当然还有notify,但是notify没有必要写在接口中,它可以在程序里直接使用。

这个系列的类设计的很规整,从上图的结构就能看出来,图中省略了write和connection的图,它们分别和read与close一致。由于这个类的操作没有那么复杂,继承关系也没有那么多层,所以这里面都没有用到Abstract的类来做具体实现。

下面我们来看这里面最核心的一个类DefaultIoFuture。这个类实现IoFuture接口,主要实现了对await和join的操作,以及处理死锁的操作。我们先看这个类关联到的成员变量,都比较简单:

01 /**
A number of seconds to wait between two deadlock controls ( 5 seconds ) */
02     private static final long DEAD_LOCK_CHECK_INTERVAL
= 5000L;
03  
04     /**
The associated session */
05     private final IoSession
session;
06  
07     /**
A lock used by the wait() method */
08     private final Object
lock;
09  
10     private IoFutureListener<?>
firstListener;
11  
12     private List<IoFutureListener<?>>
otherListeners;
13  
14     private Object
result;
15  
16     private boolean ready;
17  
18     private int waiters;

在我看来,读源码的目的,一是为了理清框架的设计逻辑,理清结构,学习这些关联关系;二是为了学习处理细节,比如死锁的处理、线程安全的处理。在这个类中,我们将看到mina作者是如何处理死锁的问题的。

我们先看await操作,await主要是为了等待异步操作的完成,然后通知相关的listener。我们先看一个简单的await操作和验证死锁的操作:

01 public IoFuture
await() 
throws InterruptedException
{
02         synchronized (lock)
{
03             while (!ready)
{
04                 waiters++;
05                 try {
06                     lock.wait(DEAD_LOCK_CHECK_INTERVAL);
07                 finally {
08                     waiters--;
09                     if (!ready)
{
10                         checkDeadLock();
11                     }
12                 }
13             }
14         }
15         return this;
16 }

我们应该要注意下在await方法中的wait操作,这里讲些题外话,面试中常问的wait和sleep的区别。wait的操作其实很规范,必须写在synchronized块内,必须由其他线程来notify,同时wait释放锁,不占资源。而sleep占着cup的资源区睡眠,时间没到不能被唤醒,只能通过中断来打断。在这个await方法中,wait了check
dead lock的时间,并且设置了计数器waiters。这个waiters在setValue方法中被运用到,在setValue中:

01 public void setValue(Object
newValue) {
02         synchronized (lock)
{
03             //
Allow only once.
04             if (ready)
{
05                 return;
06             }
07  
08             result
= newValue;
09             ready
true;
10             if (waiters
0)
{
11                 lock.notifyAll();
12             }
13         }
14  
15         notifyListeners();
16     }

异步操作是没有一个固定的顺序,谁先做好谁就返回,所以一旦有异步任务完成了操作,就会notify所有的等待,让接下来先抢到的线程再执行。在DefaultIoFuture这个类中,我觉得最重要的到不是连接或者读写,而是上面提到的setValue和getValue,因为在后续的继承关系中,会不断的用到这两个方法。不仅在后续的继承关系中,这两个方法真正在传递值得操作是发生在IoService中,不要忘了虽然session很重要,但真正起连接作用的还是service。

然后我们再看下上面提到的check dead lock的方法,在抢占中只有读、写和连接会产生死锁的情况:

01 private void checkDeadLock()
{
02         if (!(this instanceof CloseFuture
|| 
this instanceof WriteFuture
|| 
this instanceof ReadFuture
|| 
this instanceof ConnectFuture))
{
03             return;
04         }
05         StackTraceElement[]
stackTrace = Thread.currentThread().getStackTrace();
06  
07         //
Simple and quick check.
08         for (StackTraceElement
s : stackTrace) {
09             if (AbstractPollingIoProcessor.class.getName().equals(s.getClassName()))
{
10                 IllegalStateException
e = 
new IllegalStateException("t");
11                 e.getStackTrace();
12                 throw new IllegalStateException("DEAD
LOCK: "
 +
IoFuture.
class.getSimpleName()
13                         ".await()
was invoked from an I/O processor thread.  "
 "Please
use "
14                         +
IoFutureListener.
class.getSimpleName()
"
or configure a proper thread model alternatively."
);
15             }
16         }
17  
18         //
And then more precisely.
19         for (StackTraceElement
s : stackTrace) {
20             try {
21                 Class<?>
cls = DefaultIoFuture.
class.getClassLoader().loadClass(s.getClassName());
22                 if (IoProcessor.class.isAssignableFrom(cls))
{
23                     throw new IllegalStateException("DEAD
LOCK: "
 +
IoFuture.
class.getSimpleName()
24                             ".await()
was invoked from an I/O processor thread.  "
 "Please
use "
25                             +
IoFutureListener.
class.getSimpleName()
26                             "
or configure a proper thread model alternatively."
);
27                 }
28             catch (Exception
cnfe) {
29                 //
Ignore
30             }
31         }
32     }

在追踪堆栈信息时,这里采用了两种check方式,简单和精确。在简单检测中,只是对比了类名,也就是对当前类有效,是一个一对一的比较。而在精确的检测中,采用isAssignableFrom方法来分别和其父类和本类进行比较。如果有死锁,就抛异常。另外join方法被废弃,由awaitUninterruptibly代替,虽然叫join,其实还是一种wait操作,等到一定时间将flag转变一下。

下面我们看ReadFuture接口,这个接口直接继承IoFuture接口,并添加了相关的写操作。接口由DefaultReadFuture实现。在使用中,ReadFuture在IoSession中read方法中被使用,也可以说,在session层,直接读写的是future,我们再看下AbstractIoSession中的read代码:

01 public final ReadFuture
read() {
02         if (!getConfig().isUseReadOperation())
{
03             throw new IllegalStateException("useReadOperation
is not enabled."
);
04         }
05  
06         Queue<ReadFuture>
readyReadFutures = getReadyReadFutures();
07         ReadFuture
future;
08         synchronized (readyReadFutures)
{
09             future
= readyReadFutures.poll();
10             if (future
!= 
null)
{
11                 if (future.isClosed())
{
12                     //
Let other readers get notified.
13                     readyReadFutures.offer(future);
14                 }
15             else {
16                 future
new DefaultReadFuture(this);
17                 getWaitingReadFutures().offer(future);
18             }
19         }
20  
21         return future;
22     }

每次都是从队列中拿出一个future,同理,每次写也是往队列里写入一个future。在DefaultReadFuture中的方法都比较简单,这里就不贴出来了。另外WriteFuture和DefaultWriteFuture和read类似,也不再赘述。

最后我们看看ConnectFuture,我们常常写这么一段话来拿到session:

1 ConnectFuture
future = connector.connect(
new InetSocketAddress(
2                     HOST,
PORT));
//
创建连接
3             future.awaitUninterruptibly();//
等待连接创建完成
4             session
= future.getSession();
//
获得session

提一点,在多态的使用上,ConnectFuture完全可以换成IoFuture,这对后面的代码没有一点儿影响,getSession本身就是继承自IoFuture的。ConnectFuture接口由DefaultConnectFuture来具体实现,由于继承了DefaultIoFuture,所以这里面用到最多的就是DefaultIoFuture中的setValue和getValue方法,上面我们也特别强调了这两个方法的重要性,通过对result(setValue)的传递实现了对session、exception等状态的传递。

稍微总结一下future对异步的贡献,官方对future的描述就是处理了异步操作,从源码中我们很明显的可以看到future是通过await和notify来控制操作的连续性,通过死锁检测来做wait时的保障,上层(session)通过队列来缓冲各种任务,然后通过竞争,谁抢到了线程,谁就执行。Future不难,组织结构也很清楚,我觉得看这节的源代码最主要的还是要做好两点,第一是搞懂什么是异步,第二是要明白future为异步贡献了什么。

下一篇就要讲mina中最庞大的filter chain了,这是mina中代码最多,也是最具特色的一部分。

Mina源码阅读笔记(六)—Mina异步IO的实现IoFuture的更多相关文章

  1. Mina源码阅读笔记(四)—Mina的连接IoConnector2

    接着Mina源码阅读笔记(四)-Mina的连接IoConnector1,,我们继续: AbstractIoAcceptor: 001 package org.apache.mina.core.rewr ...

  2. Mina源码阅读笔记(一)-整体解读

    今天的这一节,将从整体上对mina的源代码进行把握,网上已经有好多关于mina源码的阅读笔记,但好多都是列举了一下每个接口或者类的方法.我倒是想从mina源码的结构和功能上对这个框架进行剖析.源码的阅 ...

  3. Mina源码阅读笔记(二)- IoBuffer的封装

    在阅读IoBuffer源码之前,我们先看Mina对IoBuffer的描述:A byte buffer used by MINA applications. This is a replacement ...

  4. Mina源码阅读笔记(七)—Mina的拦截器FilterChain

    Filter我们很熟悉,在Mina中,filter chain的用法也类似于Servlet的filters,这种拦截器的设计思想能够狠轻松的帮助我们实现对资源的统一处理.我们先大致连接下mina中的f ...

  5. Mina源码阅读笔记(四)—Mina的连接IoConnector1

    上一篇写的是IoAcceptor是服务器端的接收代码,今天要写的是IoConnector,是客户端的连接器.在昨天,我们还留下一些问题没有解决,这些问题今天同样会产生,但是都要等到讲到session的 ...

  6. Mina源码阅读笔记(三)-Mina的连接IoAccpetor

    其实在mina的源码中,IoService可以总结成五部分service责任.Processor线程处理.handler处理器.接收器和连接器,分别对应着IoService.IoProcessor.I ...

  7. Mina源码阅读笔记(五)—Mina对连接的操作IoSession

    IoSession是Mina管理两端的一个重要部分,也是Mina的核心,Session具有了生命周期的概念,它的生命周期和连接时紧密相关的,这点在后面的介绍中会涉及.另外,好像hibernate中也有 ...

  8. jdk源码阅读笔记-LinkedHashMap

    Map是Java collection framework 中重要的组成部分,特别是HashMap是在我们在日常的开发的过程中使用的最多的一个集合.但是遗憾的是,存放在HashMap中元素都是无序的, ...

  9. faster rcnn源码阅读笔记1

    自己保存的源码阅读笔记哈 faster rcnn 的主要识别过程(粗略) (开始填坑了): 一张3通道,1600*1600图像输入中,经过特征提取网络,得到100*100*512的feature ma ...

随机推荐

  1. 移动开发测试工具——Bugtags的集成

    移动开发测试工具--Bugtags 官网:https://bugtags.com/ 注册开发者账号 注册账号并激活邮箱,都会就不多做介绍了. 创建应用 创建完账号以后会提示添加应用,点击添加 添加应用 ...

  2. Android初级教程:使用xml序列器

    之前备份短信的时候生成xml都是手动拼写的,有一个问题:当短信里面存在</body>这样的标签的时候,最后结果就不是完整的xml文件,显然出错.但是,今天使用序列化器的方式,就能有效的解决 ...

  3. Android初级教程对大量数据的做分页处理理论知识

    有时候要加载的数据上千条时,页面加载数据就会很慢(数据加载也属于耗时操作).因此就要考虑分页甚至分批显示.先介绍一些分页的理论知识.对于具体用在哪里,会在后续博客中更新. 分页信息 1,一共多少条数据 ...

  4. CUDA5.5 的环境变量设置

    为了方便,我写了这个文件用于设置cuda5.5的环境变量. 其中有些环境变量可能用不到,大家根据需要修改就是了. export CUDA_HOME=/usr/local/cuda-5.5 export ...

  5. UNIX网络编程——关于socket阻塞与非阻塞情况下的recv、send、read、write返回值

    1.阻塞模式与非阻塞模式下recv的返回值各代表什么意思?有没有 区别?(就我目前了解阻塞与非阻塞recv返回值没有区分,都是 <0:出错,=0:连接关闭,>0接收到数据大小,特别:返回 ...

  6. CentOS上PHP完全卸载

    想把PHP卸载干净,直接用yum的remove命令是不行的,需要查看有多少rpm包,然后按照依赖顺序逐一卸载. 1.首先查看机器上安装的所有php相关的rpm包 [root@localhost ngi ...

  7. UNIX网络编程——TCP回射服务器/客户端程序

    下面通过最简单的客户端/服务器程序的实例来学习socket API. serv.c 程序的功能是从客户端读取字符然后直接回射回去: #include<stdio.h> #include&l ...

  8. 【嵌入式开发】 嵌入式开发工具简介 (裸板调试示例 | 交叉工具链 | Makefile | 链接器脚本 | eclipse JLink 调试环境)

    作者 : 韩曙亮 博客地址 : http://blog.csdn.net/shulianghan/article/details/42239705  参考博客 : [嵌入式开发]嵌入式 开发环境 (远 ...

  9. AngularJS进阶(四十)创建模块、服务

    AngularJS进阶(四十)创建模块.服务 学习要点 使用模块构架应用 创建和使用服务 为什么要使用和创建服务与模块? 服务允许你打包可重用的功能,使之能在此应用中使用. 模块允许你打包可重用的功能 ...

  10. (NO.00003)iOS游戏简单的机器人投射游戏成形记(三)

    接下来我们建立机器人对象. 在Sprites文件夹中新建Robot.ccb文件,类型为Node. 打开SpriteBuilder的Tileless View将机器人身体和手臂拖入根节点,调整好相对的位 ...