Mina-2.0.7源码学习 (4) --- IoFuture

      Mina使用IoFuture提供了I/O读写,会话创建销毁等操作的异步实现方式,它通常与某个IoSession关联使用。IoFuture主要是通过Jwait/notify机制实现异步I/O操作.在Mina源码中,其包含在org.apache.mina.core.future包中。在IoFuture的实现中,分别提供了读、写、连接、关闭的future,通过这四个future来实现异步操作:ReadFuture,WriteFuture,CloseFuture,ConnectFuture,它们本身为接口类型,都继承自IoFuture接口。类DefaultIoFuture实现了IoFuture接口,它作为一个模板,被DefaultReadFuture、DefaultWriteFuture、DefaultCloseFuture、DefaultConnectFuture继承并覆盖某些方法。除此之外,包中的IoFutureListener接口提供了消息通知当异步I/O操作完成的时候,观察者模式。


      DefaultIoFuture类中的await/await0和setValue方法是实现异步I/O操作的核心:

    public IoFuture await() throws InterruptedException {
        synchronized (lock) {
            while (!ready) {
                waiters++;
                try {
                    // Wait for a notify, or if no notify is called,
                    // assume that we have a deadlock and exit the 
                    // loop to check for a potential deadlock.
                    lock.wait(DEAD_LOCK_CHECK_INTERVAL);
                } finally {
                    waiters--;
                    if (!ready) {
                        checkDeadLock();
                    }
                }
            }
        }
        return this;
    }

      调用await讲调用lock.wait方法阻塞等待DEAD_LOCK_CHECK_INTERVAL毫秒的时间,如果返回后时间还未发生,就需要检测死锁。await0差不多,只是增加了用户定义的阻塞时间。调用await/await0之后需要通过setValue方法激活:

   /**
     * Sets the result of the asynchronous operation, and mark it as finished.
     */
    public void setValue(Object newValue) {
        synchronized (lock) {
            // Allow only once.
            if (ready) {
                return;
            }

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

        notifyListeners();
    }
      主要是通过调用lock.notifyAll通知所有阻塞在lock对象上的进程,还会调用notifyListeners通知其它通过Listener注册的监听器。
    private void checkDeadLock() {
        // Only read / write / connect / write future can cause dead lock. 
        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
            }
        }
    }
      上面的checkDeadLock死锁检测方法,主要通过从线程调用堆栈中查找是否有循环调用来判断是否发生了死锁。根据两个if逻辑,只要线程堆栈中存在调用类AbstractPollingIoProcessor或者某个IoProcessor的派生类,就认为出现了循环调用,从而出现死锁。对此自己的理解是,在用户代码中,有可能在自定义的IoHandler中重写MessageRecevied(s,message)方法或者其它某个由IoProcessor线程调用的方法时,使用了ReadFuture future = session.read(); future.await(),从而导致processor线程阻塞,而processor线程还在等待这个MessageReceived返回然后调用setValue()方法才能唤醒阻塞在future.await()上的processor,从而死锁了。求知道正解的高手指正!

      从上面可以看出,调用IoSession的read()方法返回一个ReadFuture,然后调用await()方法阻塞等待读事件完成,所以异步是指调用过程不阻塞,但是等待消息通知的过程可能会阻塞。其它类型的Future与IoSession的关联在抽象类AbstractIoSession中可以查看

    public WriteFuture write(Object message) {
        return write(message, null);
    }

    public final CloseFuture close() {
        synchronized (lock) {
            if (isClosing()) {
                return closeFuture;
            }

            closing = true;
        }

        getFilterChain().fireFilterClose();
        return closeFuture;
    }
      在AbstractIoSession中还提供了方法用于通知等待的Future,它们是调用相关的setXXX方法实现
    public final void offerReadFuture(Object message) {
        newReadFuture().setRead(message);
    }

    public final void offerFailedReadFuture(Throwable exception) {
        newReadFuture().setException(exception);
    }

    public final void offerClosedReadFuture() {
        Queue<ReadFuture> readyReadFutures = getReadyReadFutures();
        synchronized (readyReadFutures) {
            newReadFuture().setClosed();
        }
    }
      AbstractIoSession中为异步读写提供了两个队列,准备读和等待读队列。每次都是从准备读队列中取出一个ReadFuture
    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;
    }
      每次首先调用getReadyReadFutures()得到一个等待读队列,其中为了线程安全,使用了ConcurrentHashMap的putIfAbsent()方法
        public Object setAttributeIfAbsent(IoSession session, Object key, Object value) {
            if (key == null) {
                throw new IllegalArgumentException("key");
            }

            if (value == null) {
                return null;
            }

            return attributes.putIfAbsent(key, value);
        }
      接着从readyReadFutures队列中获取一个ReadFuture,如果为null(最初始的情况),那么就创建一个ReadFuture加入到waitingReadFutures队列等待,waitingReadFutures队列用来存储有多少个Future等待完成被通知,所以没处理完一个ReadFuture,都会从waitingReadFutures取一个ReadFuture并调用setRead()通知等待这个ReadFuture的线程。当readyReadFutures不为空,取出一个ReadFuture,且这个ReadFuture没有关闭,就返回这个ReadFuture,如果已经关闭了,就重新放入到readyReadFutures队列尾部(不知为什么要这样做,不是应该直接删除继续取下一个么)。
    private ReadFuture newReadFuture() {
        Queue<ReadFuture> readyReadFutures = getReadyReadFutures();
        Queue<ReadFuture> waitingReadFutures = getWaitingReadFutures();
        ReadFuture future;
        synchronized (readyReadFutures) {
            future = waitingReadFutures.poll();
            if (future == null) {
                future = new DefaultReadFuture(this);
                readyReadFutures.offer(future);
            }
        }
        return future;
    }
      每次Read完成调用offerReadFuture()方法都会调用上面的newReadFuture()得到一个正在等待的ReadFuture, 过程是直接从waitingReadFutures中取,取出来ReadFuture调用setRead()通知调用ReadFuture.await()而阻塞等待的线程。如果取出来的为空,表示还没有一个异步读操作在进行,那么就创建一个ReadFuture放入到readyReadFutures队列中,此时调用setRead由于waiters为0,不会调用lock.notifyAll,result=value, ready=true,当第二次调用setValue时候,会直接return. 后面调用这个ReadFuture的await方法会直接返回,不会阻塞等待。
    public void setRead(Object message) {
        if (message == null) {
            throw new IllegalArgumentException("message");
        }
        setValue(message);
    }

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

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

        notifyListeners();
    }
      默认情况下,在server端是不适用IoSession.read()来实现异步操作的,因为当server端session数目巨大,每一个Read操作都使用异步读作为一个ReadFuture放入到队列中阻塞等待,那么可能发生内存泄露,通常ReadFuture都是在client端使用:

 IoSession session = ...;
 // useReadOperation must be enabled to use read operation.
 session.getConfig().setUseReadOperation(true);
 
 ReadFuture future = session.read();
 // Wait until a message is received.
 future.await();
 try {
     Object message = future.getMessage();
 } catch (Exception e) {
     ...
 }

      对于异步写操作WriteFuture:

    public WriteFuture write(Object message, SocketAddress remoteAddress) {
        // ******
        // Now, we can write the message. First, create a future
        WriteFuture writeFuture = new DefaultWriteFuture(this);
        WriteRequest writeRequest = new DefaultWriteRequest(message, writeFuture, remoteAddress);

        // Then, get the chain and inject the WriteRequest into it
        IoFilterChain filterChain = getFilterChain();
        filterChain.fireFilterWrite(writeRequest);
        // ******
        // Return the WriteFuture.
        return writeFuture;
    }

      将生成一个WriteRequest,封装了数据message和writeFuture以及发送的地址remoteAddress.然后传递给FilterChain最终放入该会话对应的WriteRequestQueue. 在AbstractPollingIoProcessor类中的Processor线程调用process()处理读写,一直到writeBuffer()或者writeFile()方法调用fireMessageSent(session,req):

     public void fireMessageSent(WriteRequest request) {
        session.increaseWrittenMessages(request, System.currentTimeMillis());

        try {
            request.getFuture().setWritten();    //调用setValue---lock.notifyAll
        } catch (Throwable t) {
            fireExceptionCaught(t);
        }

        Entry head = this.head;

        if (!request.isEncoded()) {
            callNextMessageSent(head, session, request);
        }
    }

      网上找的一个使用了ConnectFuture和ReadFuture的工具类,供学习参考:  

      REF:   http://www.open-open.com/lib/view/open1350613486024.html




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值