NIO源码阅读笔记 select方法(三)

select方法的实现在SelectorImpl类中,有两种实现方式,无参的和单参的,无参方法直接调用单参方法,默认传入0:

	public int select(long var1) throws IOException {
        if (var1 < 0L) {
            throw new IllegalArgumentException("Negative timeout");
        } else {
            return this.lockAndDoSelect(var1 == 0L ? -1L : var1);
        }
    }
    
	public int select() throws IOException {
        return this.select(0L);
    }

对传入的传参数进行判断,如果为负数则抛出异常;否则调用lockAndDoSelect,传入的参数是三目运算之后的结果:

	private int lockAndDoSelect(long var1) throws IOException {
        synchronized(this) {
            if (!this.isOpen()) {
                throw new ClosedSelectorException();
            } else {
                Set var4 = this.publicKeys;
                int var10000;
                synchronized(this.publicKeys) {
                    Set var5 = this.publicSelectedKeys;
                    synchronized(this.publicSelectedKeys) {
                        var10000 = this.doSelect(var1);
                    }
                }

                return var10000;
            }
        }
    }

首先在同步锁中判断selector是否打开,如果关闭则抛出异常。isOpen方法在AbstractSelector类中实现:

	private AtomicBoolean selectorOpen = new AtomicBoolean(true);
	public final boolean isOpen() {
        return selectorOpen.get();
    }

可以看到用的是原子化的Boolean并默认为true。
回到lockAndDoSelect方法,如果selector处于开启状态,则先后以publicKeys和publicSelectedKeys为锁执行doSelect方法。
publicKeys是供外部访问的SelectionKey集合,publicSelectedKeys是供外部访问并且已经就绪的SelectionKey集合。
doSelect方法的实现在WindowsSelectorImpl类中:

protected int doSelect(long var1) throws IOException {
        if (this.channelArray == null) {
            throw new ClosedSelectorException();
        } else {
            this.timeout = var1;
            this.processDeregisterQueue();
            if (this.interruptTriggered) {
                this.resetWakeupSocket();
                return 0;
            } else {
                this.adjustThreadsCount();
                this.finishLock.reset();
                this.startLock.startThreads();

                try {
                    this.begin();

                    try {
                        this.subSelector.poll();
                    } catch (IOException var7) {
                        this.finishLock.setException(var7);
                    }

                    if (this.threads.size() > 0) {
                        this.finishLock.waitForHelperThreads();
                    }
                } finally {
                    this.end();
                }

                this.finishLock.checkForException();
                this.processDeregisterQueue();
                int var3 = this.updateSelectedKeys();
                this.resetWakeupSocket();
                return var3;
            }
        }
    }

首先判断SelectionKeyImpl数组channelArray是否为空,如果为空则抛出异常。紧接着为timeout参数赋值;然后调用processDeregisterQueue取消准备撤销的集合。
下面是该集合和cancel(将SelectionKey加入待撤销集合)方法:

	private final Set<SelectionKey> cancelledKeys = new HashSet<SelectionKey>();

    void cancel(SelectionKey k) {                       // package-private
        synchronized (cancelledKeys) {
            cancelledKeys.add(k);
        }
    }

processDeregisterQueue方法的代码如下:

void processDeregisterQueue() throws IOException {
        Set var1 = this.cancelledKeys();
        synchronized(var1) {
            if (!var1.isEmpty()) {
                Iterator var3 = var1.iterator();

                while(var3.hasNext()) {
                    SelectionKeyImpl var4 = (SelectionKeyImpl)var3.next();

                    try {
                        this.implDereg(var4);
                    } catch (SocketException var11) {
                        throw new IOException("Error deregistering key", var11);
                    } finally {
                        var3.remove();
                    }
                }
            }

        }
    }

首先获取集合并加锁,判断是否有请求取消的,若有那么就进行遍历,实际的取消操作由implDereg方法执行,最后再从集合中删除这个SelectionKeyImpl对象。
implDereg方法在WindowsSelectorImpl类中实现:

	protected void implDereg(SelectionKeyImpl var1) throws IOException {
        int var2 = var1.getIndex();

        assert var2 >= 0;

        Object var3 = this.closeLock;
        synchronized(this.closeLock) {
            if (var2 != this.totalChannels - 1) {
                SelectionKeyImpl var4 = this.channelArray[this.totalChannels - 1];
                this.channelArray[var2] = var4;
                var4.setIndex(var2);
                this.pollWrapper.replaceEntry(this.pollWrapper, this.totalChannels - 1, this.pollWrapper, var2);
            }

            var1.setIndex(-1);
        }

        this.channelArray[this.totalChannels - 1] = null;
        --this.totalChannels;
        if (this.totalChannels != 1 && this.totalChannels % 1024 == 1) {
            --this.totalChannels;
            --this.threadsCount;
        }

        this.fdMap.remove(var1);
        this.keys.remove(var1);
        this.selectedKeys.remove(var1);
        this.deregister(var1);
        SelectableChannel var7 = var1.channel();
        if (!var7.isOpen() && !var7.isRegistered()) {
            ((SelChImpl)var7).kill();
        }

    }

首先获取保存在SelectionKeyImpl 中的下标,判断该下标是否是channelArray数组中的最后一个元素;如果不是最后一个,则用数组中最后一个元素覆盖到当前位置并修改其SelectionKeyImpl 对象中保存的数组下标以及pollWrapper数组中对应的描述符和事件。
无论该SelectionKeyImpl对象是否是最后一个,都将其下标置为-1,防止再次访问。然后将数组最后一个元素赋值为null,totalChannels进行自减。接下来判断是否要将轮询线程数减一(这个和增加channel时判断轮询线程数是否要加1相同,在后面进行解释)。
然后在fdMap中移除掉该SelectionKeyImpl和Channel的描述符映射(,keys和selectedKeys中同样也需要移除(keys所有注册了的SelectionKey集合,selectedKeys是所有有事件就绪的SelectionKey集合)。
接下来开始对SelectionKeyImpl对象进行注销,deregister方法在AbstractSelector类中实现:

	protected final void deregister(AbstractSelectionKey key) {
        ((AbstractSelectableChannel)key.channel()).removeKey(key);
    }

可以看到获取了channel并调用了AbstractSelectableChannel的removeKey方法:

	void removeKey(SelectionKey k) {                    // package-private
        synchronized (keyLock) {
            for (int i = 0; i < keys.length; i++)
                if (keys[i] == k) {
                    keys[i] = null;
                    keyCount--;
                }
            ((AbstractSelectionKey)k).invalidate();
        }
    }

从keys中移除的逻辑比较简单,紧接着调用AbstractSelectionKey类中的invalidate方法:

	void invalidate() {                                 // package-private
        valid = false;
    }

回到implDereg,检查SelectionKeyImpl对象中保存的Channel对象的是否开启或注册了SelectionKey,如果都没有则调用kill方法,kill方法在不同的channel中实现方式不同。以SocketChannelImpl中的实现为例:

	private static final int ST_UNINITIALIZED = -1;// 尚未初始化
    private static final int ST_UNCONNECTED = 0;// 尚未建立连接
	private static final int ST_PENDING = 1;// 未决状态
	private static final int ST_CONNECTED = 2;// 连接状态
	private static final int ST_KILLPENDING = 3;// KILL的未决状态
	private static final int ST_KILLED = 4;// KILL状态
	private int state = -1;
	public void kill() throws IOException {
        Object var1 = this.stateLock;
        synchronized(this.stateLock) {
            if (this.state != 4) {
                if (this.state == -1) {
                    this.state = 4;
                } else {
                    assert !this.isOpen() && !this.isRegistered();

                    if (this.readerThread == 0L && this.writerThread == 0L) {
                        nd.close(this.fd);
                        this.state = 4;
                    } else {
                        this.state = 3;
                    }

                }
            }
        }
    }

首先有六种状态,上边代码中已经注释清楚了。在同步块中首先判断是否是ST_KILLED状态,只有不是 ST_KILLED状态才有必要进行操作。如果是ST_UNINITIALIZED状态则直接改为ST_KILLED状态即可。否则断言后根据读写的线程数进行判断,如果读写操作已经全部完成则将状态变更为ST_KILLED,否则变更为ST_KILLPENDING。

接下来回到doSelect方法,在处理完待注销队列之后,判断是否触发了中断:

private volatile boolean interruptTriggered = false;

如果触发了中断,调用resetWakeupSocket方法,在调用close或者wakeup方法时interruptTriggered会被赋值为true。
先看wakeup方法:

	public Selector wakeup() {
        Object var1 = this.interruptLock;
        synchronized(this.interruptLock) {
            if (!this.interruptTriggered) {
                this.setWakeupSocket();
                this.interruptTriggered = true;
            }

            return this;
        }
    }

如果当前没有触发中断,则调用setWakeupSocket方法之后interruptTriggered 赋值为true。
setWakeupSocket方法:

	private void setWakeupSocket() {
        this.setWakeupSocket0(this.wakeupSinkFd);
    }
	private native void setWakeupSocket0(int var1);

在selector创建时生成过两个SocketChannel,一个SourceChannelImpl和一个SinkChannelImpl,wakeupSinkFd就是SinkChannelImpl的句柄。
setWakeupSocket0的实现:

Java_sun_nio_ch_WindowsSelectorImpl_setWakeupSocket0(JNIEnv *env, jclass this,
                                                jint scoutFd) {
    /* Write one byte into the pipe */
    const char byte = 1;
    send(scoutFd, &byte, 1, 0);
}

通过这个双向通道的sink端向source发送一个字节的数据,这样source端描述符就进入就绪状态,就能被select感知到,Selector便被唤醒。

下来看看AbstractSelector中的close方法:

	private AtomicBoolean selectorOpen = new AtomicBoolean(true);
	public final void close() throws IOException {
        boolean open = selectorOpen.getAndSet(false);
        if (!open)
            return;
        implCloseSelector();
    }

selectorOpen 是原子化的Boolean类型,并且在初始化时默认为true。首先调用AtomicBoolean中的getAndSet方法将selectorOpen的值设为false:

	public final boolean getAndSet(boolean newValue) {
        boolean prev;
        do {
            prev = get();
        } while (!compareAndSet(prev, newValue));
        return prev;
    }

回到close方法如果之前是关闭状态,则直接return;否则调用SelectorImpl中实现的implCloseSelector方法:

	public void implCloseSelector() throws IOException {
        this.wakeup();
        synchronized(this) {
            Set var2 = this.publicKeys;
            synchronized(this.publicKeys) {
                Set var3 = this.publicSelectedKeys;
                synchronized(this.publicSelectedKeys) {
                    this.implClose();
                }
            }

        }
    }

首先调用wakeup方法,接着调用implClose方法,这两个方法的实现都在WindowsSelectorImpl类中:

	protected void implClose() throws IOException {
        Object var1 = this.closeLock;
        synchronized(this.closeLock) {
            if (this.channelArray != null && this.pollWrapper != null) {
                Object var2 = this.interruptLock;
                synchronized(this.interruptLock) {
                    this.interruptTriggered = true;
                }

                this.wakeupPipe.sink().close();
                this.wakeupPipe.source().close();

                for(int var7 = 1; var7 < this.totalChannels; ++var7) {
                    if (var7 % 1024 != 0) {
                        this.deregister(this.channelArray[var7]);
                        SelectableChannel var3 = this.channelArray[var7].channel();
                        if (!var3.isOpen() && !var3.isRegistered()) {
                            ((SelChImpl)var3).kill();
                        }
                    }
                }

                this.pollWrapper.free();
                this.pollWrapper = null;
                this.selectedKeys = null;
                this.channelArray = null;
                Iterator var8 = this.threads.iterator();

                while(var8.hasNext()) {
                    WindowsSelectorImpl.SelectThread var9 = (WindowsSelectorImpl.SelectThread)var8.next();
                    var9.makeZombie();
                }

                this.startLock.startThreads();
            }

        }
    }

在同步块中首先通过channelArray 和pollWrapper的状态判断是否有需要关闭的资源,如果有需要关闭的资源,首先在同步块中设置中断为true,然后关闭创建selector时打开的两个channel,然后在循环中关闭不活跃的channel。接下来是对SelectThread的处理,放在后面进行分析。
下面回到doSelect方法,如果已经触发了中断,调用resetWakeupSocket方法:

	private void resetWakeupSocket() {
        Object var1 = this.interruptLock;
        synchronized(this.interruptLock) {
            if (this.interruptTriggered) {
                this.resetWakeupSocket0(this.wakeupSourceFd);
                this.interruptTriggered = false;
            }
        }
    }
	private native void resetWakeupSocket0(int var1);

resetWakeupSocket0也是一个native方法,和setWakeupSocket0正好互补,用来读取setWakeupSocket0中发送的数据,再将interruptTriggered设置为false,最后doSelect将会立即返回0,而不会调用poll操作。
如果doSelect方法中interruptTriggered为false,则先调用adjustThreadsCount方法调整轮询线程的数量,:

	private final List<WindowsSelectorImpl.SelectThread> threads = new ArrayList();
	private void adjustThreadsCount() {
        int var1;
        if (this.threadsCount > this.threads.size()) {
            for(var1 = this.threads.size(); var1 < this.threadsCount; ++var1) {
                WindowsSelectorImpl.SelectThread var2 = new WindowsSelectorImpl.SelectThread(var1);
                this.threads.add(var2);
                var2.setDaemon(true);
                var2.start();
            }
        } else if (this.threadsCount < this.threads.size()) {
            for(var1 = this.threads.size() - 1; var1 >= this.threadsCount; --var1) {
                ((WindowsSelectorImpl.SelectThread)this.threads.remove(var1)).makeZombie();
            }
        }

    }

SelectThread类是WindowsSelectorImpl类的内部类:

	private final class SelectThread extends Thread {
        private final int index;
        final WindowsSelectorImpl.SubSelector subSelector;
        private long lastRun;
        private volatile boolean zombie;

        private SelectThread(int var2) {
            this.lastRun = 0L;
            this.index = var2;
            this.subSelector = WindowsSelectorImpl.this.new SubSelector(var2);
            this.lastRun = WindowsSelectorImpl.this.startLock.runsCounter;
        }

        void makeZombie() {
            this.zombie = true;
        }

        boolean isZombie() {
            return this.zombie;
        }

        public void run() {
            for(; !WindowsSelectorImpl.this.startLock.waitForStart(this); WindowsSelectorImpl.this.finishLock.threadFinished()) {
                try {
                    this.subSelector.poll(this.index);
                } catch (IOException var2) {
                    WindowsSelectorImpl.this.finishLock.setException(var2);
                }
            }

        }
    }

在channel注册时channel的总数每达到1024的倍数,轮询线程数会加一。index是当前SelectThread在ArrayList threads中的下标,lastRun在后边用到的时候再说,zombie用来表示状态;

如果channel的总数小于1024,threads为0,轮询由WindowsSelectorImpl来执行:

 private final WindowsSelectorImpl.SubSelector subSelector = new WindowsSelectorImpl.SubSelector();

如果channel的总数大于1024,则需要WindowsSelectorImpl和SelectThread对象开启新线程一起执行轮询。

SubSelector 也是一个内部类(只复制一些在本文中用的到的代码):

 private final class SubSelector {
        private final int pollArrayIndex;
        private final int[] readFds;
        private final int[] writeFds;
        private final int[] exceptFds;

        private SubSelector() {
            this.readFds = new int[1025];
            this.writeFds = new int[1025];
            this.exceptFds = new int[1025];
            this.pollArrayIndex = 0;
        }

        private SubSelector(int var2) {
            this.readFds = new int[1025];
            this.writeFds = new int[1025];
            this.exceptFds = new int[1025];
            this.pollArrayIndex = (var2 + 1) * 1024;
        }

        private int poll() throws IOException {
            return this.poll0(WindowsSelectorImpl.this.pollWrapper.pollArrayAddress, Math.min(WindowsSelectorImpl.this.totalChannels, 1024), this.readFds, this.writeFds, this.exceptFds, WindowsSelectorImpl.this.timeout);
        }

        private int poll(int var1) throws IOException {
            return this.poll0(WindowsSelectorImpl.this.pollWrapper.pollArrayAddress + (long)(this.pollArrayIndex * PollArrayWrapper.SIZE_POLLFD), Math.min(1024, WindowsSelectorImpl.this.totalChannels - (var1 + 1) * 1024), this.readFds, this.writeFds, this.exceptFds, WindowsSelectorImpl.this.timeout);
        }

        private native int poll0(long var1, int var3, int[] var4, int[] var5, int[] var6, long var7);

        ...
}

一共给出了两种构造方法,区别在于对pollArrayIndex成员的赋值。poll方法的单参和无参两种实现方式都调用了poll0方法,poll0方法的第一个参数是轮询时在内存中的起始位置,第二个参数是线程应负责轮询的channel的总数。
第三到五个参数传入的:readFds 、writeFds、exceptFds 分别对应读、写、异常描述符 ,在SubSelector构造中初始化大小都是1025,多出来的一个就是前面说过的wakeupSourceFd描述符,用于唤醒,所以是1025。
最后一个参数是超时时间,在调用select单参方法时传入。

除此之外还有两个内部类StartLock和FinishLock:

	private final WindowsSelectorImpl.StartLock startLock = new WindowsSelectorImpl.StartLock();
    private final WindowsSelectorImpl.FinishLock finishLock = new WindowsSelectorImpl.FinishLock();
	private final class StartLock {
        private long runsCounter;

        private StartLock() {
        }

        private synchronized void startThreads() {
            ++this.runsCounter;
            this.notifyAll();
        }

        private synchronized boolean waitForStart(WindowsSelectorImpl.SelectThread var1) {
            while(this.runsCounter == var1.lastRun) {
                try {
                    WindowsSelectorImpl.this.startLock.wait();
                } catch (InterruptedException var3) {
                    Thread.currentThread().interrupt();
                }
            }

            if (var1.isZombie()) {
                return true;
            } else {
                var1.lastRun = this.runsCounter;
                return false;
            }
        }
    }
	private final class FinishLock {
        private int threadsToFinish;
        IOException exception;

        private FinishLock() {
            this.exception = null;
        }

        private void reset() {
            this.threadsToFinish = WindowsSelectorImpl.this.threads.size();
        }

        private synchronized void threadFinished() {
            if (this.threadsToFinish == WindowsSelectorImpl.this.threads.size()) {
                WindowsSelectorImpl.this.wakeup();
            }

            --this.threadsToFinish;
            if (this.threadsToFinish == 0) {
                this.notify();
            }

        }

        private synchronized void waitForHelperThreads() {
            if (this.threadsToFinish == WindowsSelectorImpl.this.threads.size()) {
                WindowsSelectorImpl.this.wakeup();
            }

            while(this.threadsToFinish != 0) {
                try {
                    WindowsSelectorImpl.this.finishLock.wait();
                } catch (InterruptedException var2) {
                    Thread.currentThread().interrupt();
                }
            }

        }

        private synchronized void setException(IOException var1) {
            this.exception = var1;
        }

        private void checkForException() throws IOException {
            if (this.exception != null) {
                StringBuffer var1 = new StringBuffer("An exception occurred during the execution of select(): \n");
                var1.append(this.exception);
                var1.append('\n');
                this.exception = null;
                throw new IOException(var1.toString());
            }
        }
    }

startLock和finishLock都只有一份。

结合上边所提到的内部类继续看doSelect方法的代码,在调用adjustThreadsCount调整了线程数量之后,调用了finishLock的reset方法:

	private void reset() {
            this.threadsToFinish = WindowsSelectorImpl.this.threads.size();
        }

将threadsToFinish赋值为threads的总数,接着调用startLock的startThreads方法:

	private synchronized void startThreads() {
            ++this.runsCounter;
            this.notifyAll();
        }

对runsCounter进行自增并唤醒所有的阻塞。
接下来调用AbstractSelector类中实现的begin方法:

	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);
    }

若是中断器interruptor=null,就创建一个,当当前线程阻塞在I/O操作上并且发生了线程级别的中断时,就会调用wakeup方法唤醒Selector。

begin方法结束后先使WindowsSelectorImpl执行轮询,直接调用subSelector.poll();
接着判断链表threads的长度是否大于零,如果大于零则还需要调用.finishLock.waitForHelperThreads方法(上边说了要开启新的线程执行轮询操作):

private synchronized void waitForHelperThreads() {
            if (this.threadsToFinish == WindowsSelectorImpl.this.threads.size()) {
                WindowsSelectorImpl.this.wakeup();
            }

            while(this.threadsToFinish != 0) {
                try {
                    WindowsSelectorImpl.this.finishLock.wait();
                } catch (InterruptedException var2) {
                    Thread.currentThread().interrupt();
                }
            }

        }

this.threadsToFinish == WindowsSelectorImpl.this.threads.size()的判断是为帮助唤醒所有处于poll阻塞的轮询。结合SelectThread类中的run方法,这里的逻辑就清晰易懂了。最后调用end方法:

	protected final void end() {
        AbstractInterruptibleChannel.blockedOn(null);
    }

源码中对此方法的注释是:标记可能无限期阻塞的I / O操作的结束。

接下来是调用FinishLock中的checkForException方法对异常进行检查。之后调用processDeregisterQueue来取消可能在select轮询时发生的SelectionKeyl的撤销。这个方法之间已经说过了。
接下来调用updateSelectedKeys更新SelectedKey:

	private long updateCount = 0L;
	private int updateSelectedKeys() {
        ++this.updateCount;
        byte var1 = 0;
        int var4 = var1 + this.subSelector.processSelectedKeys(this.updateCount);

        WindowsSelectorImpl.SelectThread var3;
        for(Iterator var2 = this.threads.iterator(); var2.hasNext(); var4 += var3.subSelector.processSelectedKeys(this.updateCount)) {
            var3 = (WindowsSelectorImpl.SelectThread)var2.next();
        }

        return var4;
    }

首先对updateCount进行自增,然后调用subSelector的processSelectedKeys方法,得到poll返回的就绪的Channel描述符,也就是得到事件就绪的Channel个数,同理也就需要得到所有SelectThread中的。

其中processSelectedKeys方法如下:

	private int processSelectedKeys(long var1) {
            byte var3 = 0;
            int var4 = var3 + this.processFDSet(var1, this.readFds, Net.POLLIN, false);
            var4 += this.processFDSet(var1, this.writeFds, Net.POLLCONN | Net.POLLOUT, false);
            var4 += this.processFDSet(var1, this.exceptFds, Net.POLLIN | Net.POLLCONN | Net.POLLOUT, true);
            return var4;
        }

调用processFDSet方法处理了读写以及异常:

		private int processFDSet(long var1, int[] var3, int var4, boolean var5) {
            int var6 = 0;

            for(int var7 = 1; var7 <= var3[0]; ++var7) {
                int var8 = var3[var7];
                if (var8 == WindowsSelectorImpl.this.wakeupSourceFd) {
                    synchronized(WindowsSelectorImpl.this.interruptLock) {
                        WindowsSelectorImpl.this.interruptTriggered = true;
                    }
                } else {
                    WindowsSelectorImpl.MapEntry var9 = WindowsSelectorImpl.this.fdMap.get(var8);
                    if (var9 != null) {
                        SelectionKeyImpl var10 = var9.ski;
                        if (!var5 || !(var10.channel() instanceof SocketChannelImpl) || !WindowsSelectorImpl.this.discardUrgentData(var8)) {
                            if (WindowsSelectorImpl.this.selectedKeys.contains(var10)) {
                                if (var9.clearedCount != var1) {
                                    if (var10.channel.translateAndSetReadyOps(var4, var10) && var9.updateCount != var1) {
                                        var9.updateCount = var1;
                                        ++var6;
                                    }
                                } else if (var10.channel.translateAndUpdateReadyOps(var4, var10) && var9.updateCount != var1) {
                                    var9.updateCount = var1;
                                    ++var6;
                                }

                                var9.clearedCount = var1;
                            } else {
                                if (var9.clearedCount != var1) {
                                    var10.channel.translateAndSetReadyOps(var4, var10);
                                    if ((var10.nioReadyOps() & var10.nioInterestOps()) != 0) {
                                        WindowsSelectorImpl.this.selectedKeys.add(var10);
                                        var9.updateCount = var1;
                                        ++var6;
                                    }
                                } else {
                                    var10.channel.translateAndUpdateReadyOps(var4, var10);
                                    if ((var10.nioReadyOps() & var10.nioInterestOps()) != 0) {
                                        WindowsSelectorImpl.this.selectedKeys.add(var10);
                                        var9.updateCount = var1;
                                        ++var6;
                                    }
                                }

                                var9.clearedCount = var1;
                            }
                        }
                    }
                }
            }

            return var6;
        }

这个方法其实就是把poll0方法轮询的描述符结果放入传入的数组中,然后通过遍历这个数组,得到相应的Channel描述符,因为之前通过fdMap保存了Channel的描述符和SelectionKeyImpl的映射关系,那么就可以根据Channel描述符找到对应的SelectionKeyImpl对象,再根据传入的状态值var4来更新Channel的状态,最后将其保存在selectedKeys集合中供外部访问。

下面是网上找到的一张图,结合图片更容易理解jdk中NIO的架构。
在这里插入图片描述
对jdk中NIO源码的学习到此告一段落。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值