zoukankan      html  css  js  c++  java
  • NIO

    1. 背景

    SelectableChannel对象的多路复用器。

    可以通过调用Selector.open()方法创建Selector对象。Selector.open()方法会利用系统默认的SelectorProvider创建Selector对象。也可以通过自定义SelectorProvider对象的openSelector方法创建Selector。Selector会一直处于打开状态,直到调用它的close方法。

    SelectionKey对象表示每一个注册到Selector的SelectorChannel。每个Selector维护3个集合:

    • key集合:表示注册到该Selector的Channel,通过Channel的register方法可以往key集合中添加元素,取消的key在selection操作之间从key集合中移除,key集合不能直接修改,通过keys方法返回。
    • selected-key集合:在selection操作中,从所有的key集合中识别出已经ready的key对应的Channel。通过执行set的remove可以移除key或者执行迭代器对象的remove。否则key将不能通过其他方式移除。不可以直接增加到selected-key集合中。
    • cancelled-key集合:key已经被取消,但是对应的Channel还没有撤销,这个集合不可以直接访问,这个cancelled-key总是key集合的子集。当key被取消,close对应的Channel或执行它的cancel方法,则添加key到cancelled-key集合中。取消key将导致下一次Selection操作时它的通道被撤销,同时将从所有的Selector的key集合中删除。

    备注:新创建的Selector对象,这3个集合都是空集合。

    Selection

    在每次执行Selection操作时,key可能从selected-key集合中增加或删除,也可能从key集合和cancelled-key集合中删除。通过select(),select(long),selectNow()方法执行Selection操作,包含3个步骤:

    (1)

    2. Selector源码分析

    2.1 API

    public abstract class Selector implements Closeable {
    
        protected Selector() { }
    
        // 创建Selector对象
        public static Selector open() throws IOException {
            return SelectorProvider.provider().openSelector();
        }
    
        // 检测Selector是否打开
        public abstract boolean isOpen();
    
        // 返回创建该Selector的Provider
        public abstract SelectorProvider provider();
    
        // 返回Key集合,key集合不能被直接修改,只有在被cancel和channel被撤销的时候key才被移除。并且不是线程安全的集合。
        public abstract Set<SelectionKey> keys();
    
        // 返回selected-key集合,key可以直接移除,但是不可以直接增加。并且不是线程安全的集合。
        public abstract Set<SelectionKey> selectedKeys();
    
         // 选择channel有IO事件的key。
         // 该方法是非阻塞的selection操作,如果自上次selection操作之后无channel具有IO事件,该方法会立刻返回零。
         // 执行该方法会立刻清除之前执行的wakeup影响。
        public abstract int selectNow() throws IOException;
    
        // 阻塞操作,只有在以下的状态变化时:
        //(1)至少有一个IO的channel(2)调用selector.wakeup方法(3)当前线程被interrupt(4)timeout时间到(毫秒)
        public abstract int select(long timeout)
            throws IOException;
    
         // 阻塞操作,返回条件与select(long timeout)类似
        public abstract int select() throws IOException;
    
         // 唤醒当前select阻塞的操作:如果另一个线程当前阻塞在select或select(long)方法。
         // 如果当前没有select阻塞,则下次执行select或select(long)则直接返回,除非selectNow同时执行;
         //之后select和select(long)方法会正常阻塞;
         // 如果在select操作之间多次调用wakeup与调用一次效果是一样的
        public abstract Selector wakeup();
    
        // 关闭Selector。
        // 调用close方法,如果当前阻塞在selection操作,就像调用wakeup方法一样会立刻中断操作
        // 与该selector关联的未cancelled的key将失效,它们的channel将撤销,与Selector相关的其他资源将释放。
        // 如果Selector已经关闭,执行这个方法将没有影响。
        // selector关闭之后,如果执行与selector相关的操作会报ClosedSelectorException
        public abstract void close() throws IOException;
    
    }
    

    2.2 类图

     

    2.3 AbstractSelector

    AbstractSelector主要实现了Selector的打开关闭的状态维护,支持异步关闭和中断的begin和end方法,cancelledKeys等。

    package java.nio.channels.spi;
    
    import java.io.IOException;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.util.HashSet;
    import java.util.Set;
    import sun.nio.ch.Interruptible;
    import java.util.concurrent.atomic.AtomicBoolean;
    
    // Selector的基本实现。
    public abstract class AbstractSelector
        extends Selector
    {
    
        private AtomicBoolean selectorOpen = new AtomicBoolean(true); // 是否打开
    
        // The provider that created this selector
        private final SelectorProvider provider;
    
        protected AbstractSelector(SelectorProvider provider) {
            this.provider = provider;
        }
        // 三大key集合之一cancelledKeys
        private final Set<SelectionKey> cancelledKeys = new HashSet<SelectionKey>();
    
        void cancel(SelectionKey k) {                       // package-private
            synchronized (cancelledKeys) {
                cancelledKeys.add(k);
            }
        }
    
        public final void close() throws IOException {
            boolean open = selectorOpen.getAndSet(false);
            if (!open)
                return;
            implCloseSelector();// 只有在Selector未关闭的情况下调用,并且只能被调用一次。
        }
        // 关闭Selector
        // 这个方法被close方法调用去执行Selector的关闭操作,只有在Selector未关闭的情况下调用,并且只能被调用一次。具体参考上面close实现
        protected abstract void implCloseSelector() throws IOException;
    
        public final boolean isOpen() {
            return selectorOpen.get();
        }
    
        public final SelectorProvider provider() {
            return provider;
        }
    
        protected final Set<SelectionKey> cancelledKeys() {
            return cancelledKeys;
        }
    
        // 为Selector注册Channel,这个方法被AbstractSelectableChannel.register方法调用
        protected abstract SelectionKey register(AbstractSelectableChannel ch,
                                                 int ops, Object att);
    
        protected final void deregister(AbstractSelectionKey key) {
            ((AbstractSelectableChannel)key.channel()).removeKey(key);
        }
    
    
        // -- Interruption machinery --
    
        private Interruptible interruptor = null;
        // 支持异步关闭和中断的begin和end方法
        protected final void begin() {
            if (interruptor == null) {
                interruptor = new Interruptible() {
                        public void interrupt(Thread ignore) {
                            AbstractSelector.this.wakeup();
                        }};
            }
            AbstractInterruptibleChannel.blockedOn(interruptor);
            Thread me = Thread.currentThread();
            if (me.isInterrupted())
                interruptor.interrupt(me);
        }
    
        protected final void end() {
            AbstractInterruptibleChannel.blockedOn(null);
        }
    
    }  

    备注:支持异步关闭和中断的机制,可以参考http://www.cnblogs.com/lujiango/p/8478154.html

    2.4 SelectorImpl

    package sun.nio.ch;
    
    import java.io.IOException;
    import java.nio.channels.*;
    import java.nio.channels.spi.*;
    import java.net.SocketException;
    import java.util.*;
    import sun.misc.*;
    
    
    // Selector的基本实现
    abstract class SelectorImpl
        extends AbstractSelector
    {
    
        // 已经准备IO的keys
        protected Set<SelectionKey> selectedKeys;
    
        // 注册到该Selector的所有key
        protected HashSet<SelectionKey> keys;
    
        // Public views of the key sets
        private Set<SelectionKey> publicKeys;             // 不可变
        private Set<SelectionKey> publicSelectedKeys;     // 可删除,不可增加
    
        protected SelectorImpl(SelectorProvider sp) {
            super(sp);
            keys = new HashSet<SelectionKey>();
            selectedKeys = new HashSet<SelectionKey>();
            if (Util.atBugLevel("1.4")) {
                publicKeys = keys;
                publicSelectedKeys = selectedKeys;
            } else {
                publicKeys = Collections.unmodifiableSet(keys);
                publicSelectedKeys = Util.ungrowableSet(selectedKeys);
            }
        }
    
        public Set<SelectionKey> keys() {
            if (!isOpen() && !Util.atBugLevel("1.4"))
                throw new ClosedSelectorException();
            return publicKeys;
        }
    
        public Set<SelectionKey> selectedKeys() {
            if (!isOpen() && !Util.atBugLevel("1.4"))
                throw new ClosedSelectorException();
            return publicSelectedKeys;
        }
        // 对于Windows系统,需要WindowsSelectorImpl实现doSelect方法
        protected abstract int doSelect(long timeout) throws IOException; 
    
        private int lockAndDoSelect(long timeout) throws IOException {
            synchronized (this) {
                if (!isOpen())
                    throw new ClosedSelectorException();
                synchronized (publicKeys) {
                    synchronized (publicSelectedKeys) {
                        return doSelect(timeout);
                    }
                }
            }
        }
        // select方法的实现
        public int select(long timeout)
            throws IOException
        {
            if (timeout < 0)
                throw new IllegalArgumentException("Negative timeout");
            return lockAndDoSelect((timeout == 0) ? -1 : timeout);
        }
    
        public int select() throws IOException {
            return select(0);
        }
    
        public int selectNow() throws IOException {
            return lockAndDoSelect(0);
        }
    
        public void implCloseSelector() throws IOException {
            wakeup();
            synchronized (this) {
                synchronized (publicKeys) {
                    synchronized (publicSelectedKeys) {
                        implClose();
                    }
                }
            }
        }
        // 需要WindowsSelectorImpl实现
        protected abstract void implClose() throws IOException;
    
        void putEventOps(SelectionKeyImpl sk, int ops) { }
    
        protected final SelectionKey register(AbstractSelectableChannel ch,
                                              int ops,
                                              Object attachment)
        {
            if (!(ch instanceof SelChImpl))
                throw new IllegalSelectorException();
            SelectionKeyImpl k = new SelectionKeyImpl((SelChImpl)ch, this);
            k.attach(attachment);
            synchronized (publicKeys) {
                implRegister(k);
            }
            k.interestOps(ops);
            return k;
        }
    
        protected abstract void implRegister(SelectionKeyImpl ski);
    
        void processDeregisterQueue() throws IOException {
            // Precondition: Synchronized on this, keys, and selectedKeys
            Set cks = cancelledKeys();
            synchronized (cks) {
                if (!cks.isEmpty()) {
                    Iterator i = cks.iterator();
                    while (i.hasNext()) {
                        SelectionKeyImpl ski = (SelectionKeyImpl)i.next();
                        try {
                            implDereg(ski);
                        } catch (SocketException se) {
                            IOException ioe = new IOException(
                                "Error deregistering key");
                            ioe.initCause(se);
                            throw ioe;
                        } finally {
                            i.remove();
                        }
                    }
                }
            }
        }
    
        protected abstract void implDereg(SelectionKeyImpl ski) throws IOException;
    
        abstract public Selector wakeup(); // 定义唤醒方法
    
    }
    

      

    2.5 WindowsSelectorImpl和EPollSelectorImpl

    具体会分析windows平台的WindowsSelectorImpl,Linux平台的EPollSelectorImpl暂时不做分析。

    3. WindowsSelectorImpl

      

  • 相关阅读:
    BZOJ 3744 Gty的妹子序列
    BZOJ 3872 Ant colony
    BZOJ 1087 互不侵犯
    BZOJ 1070 修车
    BZOJ 2654 tree
    BZOJ 3243 向量内积
    1003 NOIP 模拟赛Day2 城市建设
    CF865D Buy Low Sell High
    CF444A DZY Loves Physics
    Luogu 4310 绝世好题
  • 原文地址:https://www.cnblogs.com/lujiango/p/8458214.html
Copyright © 2011-2022 走看看