Mina源码阅读笔记(六)—Mina异步IO的实现IoFuture

接上篇《IoSession对链接的操做》 java

IoFuture是和IoSession紧密相连的一个类,在官网上并无对它的描述,由于它通常不会显示的拿出来用,权当是一个工具类被session所使用。固然在做用上,这个系列可并不简单,咱们先看源码的注释对它的描述: 面试

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

这个类是提供异步操做的一个工具,因此在读源码以前,必须对异步IO操做有所了解,而后咱们才能够顺着这条路往下走。关于异步IO的介绍能够看:《同步、异步、阻塞、非阻塞》 安全

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

这个系列的类设计的很规整,从上图的结构就能看出来,图中省略了writeconnection的图,它们分别和readclose一致。因为这个类的操做没有那么复杂,继承关系也没有那么多层,因此这里面都没有用到Abstract的类来作具体实现。 框架

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

/** A number of seconds to wait between two deadlock controls ( 5 seconds ) */
    private static final long DEAD_LOCK_CHECK_INTERVAL = 5000L;

    /** The associated session */
    private final IoSession session;

    /** A lock used by the wait() method */
    private final Object lock;

    private IoFutureListener<?> firstListener;

    private List<IoFutureListener<?>> otherListeners;

    private Object result;

    private boolean ready;

    private int waiters;

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

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

public IoFuture await() throws InterruptedException {
        synchronized (lock) {
            while (!ready) {
                waiters++;
                try {
                    lock.wait(DEAD_LOCK_CHECK_INTERVAL);
                } finally {
                    waiters--;
                    if (!ready) {
                        checkDeadLock();
                    }
                }
            }
        }
        return this;
}

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

public void setValue(Object newValue) {
        synchronized (lock) {
            // Allow only once.
            if (ready) {
                return;
            }

            result = newValue;
            ready = true;
            if (waiters > 0) {
                lock.notifyAll();
            }
        }

        notifyListeners();
    }

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

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

private void checkDeadLock() {
        if (!(this instanceof CloseFuture || this instanceof WriteFuture || this instanceof ReadFuture || this instanceof ConnectFuture)) {
            return;
        }
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();

        // Simple and quick check.
        for (StackTraceElement s : stackTrace) {
            if (AbstractPollingIoProcessor.class.getName().equals(s.getClassName())) {
                IllegalStateException e = new IllegalStateException("t");
                e.getStackTrace();
                throw new IllegalStateException("DEAD LOCK: " + IoFuture.class.getSimpleName()
                        + ".await() was invoked from an I/O processor thread.  " + "Please use "
                        + IoFutureListener.class.getSimpleName() + " or configure a proper thread model alternatively.");
            }
        }

        // And then more precisely.
        for (StackTraceElement s : stackTrace) {
            try {
                Class<?> cls = DefaultIoFuture.class.getClassLoader().loadClass(s.getClassName());
                if (IoProcessor.class.isAssignableFrom(cls)) {
                    throw new IllegalStateException("DEAD LOCK: " + IoFuture.class.getSimpleName()
                            + ".await() was invoked from an I/O processor thread.  " + "Please use "
                            + IoFutureListener.class.getSimpleName()
                            + " or configure a proper thread model alternatively.");
                }
            } catch (Exception cnfe) {
                // Ignore
            }
        }
    }

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

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

public final ReadFuture read() {
        if (!getConfig().isUseReadOperation()) {
            throw new IllegalStateException("useReadOperation is not enabled.");
        }

        Queue<ReadFuture> readyReadFutures = getReadyReadFutures();
        ReadFuture future;
        synchronized (readyReadFutures) {
            future = readyReadFutures.poll();
            if (future != null) {
                if (future.isClosed()) {
                    // Let other readers get notified.
                    readyReadFutures.offer(future);
                }
            } else {
                future = new DefaultReadFuture(this);
                getWaitingReadFutures().offer(future);
            }
        }

        return future;
    }

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

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

ConnectFuture future = connector.connect(new InetSocketAddress(
					HOST, PORT));// 建立链接
			future.awaitUninterruptibly();// 等待链接建立完成
			session = future.getSession();// 得到session

提一点,在多态的使用上,ConnectFuture彻底能够换成IoFuture,这对后面的代码没有一点儿影响,getSession自己就是继承自IoFuture的。ConnectFuture接口由DefaultConnectFuture来具体实现,因为继承了DefaultIoFuture,因此这里面用到最多的就是DefaultIoFuture中的setValuegetValue方法,上面咱们也特别强调了这两个方法的重要性,经过对resultsetValue)的传递实现了对sessionexception等状态的传递。

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

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

相关文章
相关标签/搜索