java nio-Selector.md

273 阅读3分钟

架构图

1.单线程
处理多个通道 就不需要创建那么多线程 不需要线程来回切换

2.选择器
用来选择一个通道

3.通道 1)一个服务器端通道(最终对应操作系统的服务器端套接字) 2)每个客户端连接都有一个连接通道(最终对应操作系统的客户端套接字) file:///var/folders/m9/yshd99dx1g38gtyxvv3k201r0000gn/T/WizNote/6a23daf1-067c-4096-86d6-f5fe1fd20958/index_files/f82c56ef-6b45-4a04-bea0-e79f0dc40a13.jpg

类继承图

1.接口
2.抽象类
3.实现类


核心类
1.选择器Selector
2.选择器提供者SelectorProvider

源码分析

创建选择器/选择器提供者 对象

public abstract class Selector implements Closeable {



    /**

     * Initializes a new instance of this class.

     */

    protected Selector() { }



    /**

     * Opens a selector.

     *

     * <p> The new selector is created by invoking the {@link

     * java.nio.channels.spi.SelectorProvider#openSelector openSelector} method

     * of the system-wide default {@link

     * java.nio.channels.spi.SelectorProvider} object.  </p>

     *

     * @return  A new selector

     *

     * @throws  IOException

     *          If an I/O error occurs

     */

    public static Selector open() throws IOException {

        return SelectorProvider.provider().openSelector(); //KQueueSelectorProvider

    }


public class KQueueSelectorProvider

extends SelectorProviderImpl

{

    public AbstractSelector openSelector() throws IOException {

        return new KQueueSelectorImpl(this);

    }

}


public abstract class AbstractSelector

    extends Selector

{



    private AtomicBoolean selectorOpen = new AtomicBoolean(true);



    // The provider that created this selector

    private final SelectorProvider provider; //Selector和SelectorProvider:1.Selector包含了提供者SelectorProvider 2.提供者创建Selector



    /**

     * Initializes a new instance of this class.

     *

     * @param  provider

     *         The provider that created this selector

     */

    protected AbstractSelector(SelectorProvider provider) {

        this.provider = provider;

    }


注册通道到选择器

通道和选择器的关系?
通道实现了选择器 但是底层注册还是调用选择器.注册:通道.注册——》选择器.注册

public final SelectionKey register(Selector sel //选择器实现类, int ops //监听事件,

                                       Object att)

        throws ClosedChannelException

    {

        synchronized (regLock) {

            if (!isOpen())

                throw new ClosedChannelException();

            if ((ops & ~validOps()) != 0)

                throw new IllegalArgumentException();

            if (blocking)

                throw new IllegalBlockingModeException();

            SelectionKey k = findKey(sel);

            if (k != null) {

                k.interestOps(ops);

                k.attach(att);

            }

            if (k == null) {

                // New registration

                synchronized (keyLock) {

                    if (!isOpen())

                        throw new ClosedChannelException();

                    k = ((AbstractSelector)sel).register(this, ops, att); //注册

                    addKey(k);

                }

            }

            return k;

        }

    }


选择通道

public abstract class SelectorImpl
    extends AbstractSelector
{

private int lockAndDoSelect(long timeout) throws IOException {
        synchronized (this) {
            if (!isOpen())
                throw new ClosedSelectorException();
            synchronized (publicKeys) {
                synchronized (publicSelectedKeys) {
                    return doSelect(timeout); //选择
                }
            }
        }
    }
class KQueueSelectorImpl
    extends SelectorImpl
{
protected int doSelect(long timeout)
        throws IOException
    {
        int entries = 0;
        if (closed)
            throw new ClosedSelectorException();
        processDeregisterQueue(); //
        try {
            begin(); //
            entries = kqueueWrapper.poll(timeout); //阻塞:主要是里面调用kevent0会阻塞
        } finally {
            end();
        }
        processDeregisterQueue();
        return updateSelectedKeys(entries); //
    }
class KQueueArrayWrapper {

int poll(long timeout) {
        updateRegistrations(); //
        int updated = kevent0(kq, keventArrayAddress, NUM_KEVENTS, timeout); //native方法 //阻塞
        return updated;
    }

void updateRegistrations() {
        synchronized (updateList) {
            Update u = null;
            while ((u = updateList.poll()) != null) {
                SelChImpl ch = u.channel;
                if (!ch.isOpen())
                    continue;

                register0(kq, ch.getFDVal(), u.events & POLLIN, u.events & POLLOUT); //native方法 
            }
        }
    }
    
    
/**
     * Update the keys whose fd's have been selected by kqueue.
     * Add the ready keys to the selected key set.
     * If the interrupt fd has been selected, drain it and clear the interrupt.
     */
    private int updateSelectedKeys(int entries)
        throws IOException
    {
        int numKeysUpdated = 0;
        boolean interrupted = false;

        // A file descriptor may be registered with kqueue with more than one
        // filter and so there may be more than one event for a fd. The update
        // count in the MapEntry tracks when the fd was last updated and this
        // ensures that the ready ops are updated rather than replaced by a
        // second or subsequent event.
        updateCount++;

        for (int i = 0; i < entries; i++) {
            int nextFD = kqueueWrapper.getDescriptor(i);
            if (nextFD == fd0) {
                interrupted = true;
            } else {
                MapEntry me = fdMap.get(Integer.valueOf(nextFD));

                // entry is null in the case of an interrupt
                if (me != null) {
                    int rOps = kqueueWrapper.getReventOps(i);
                    SelectionKeyImpl ski = me.ski;
                    if (selectedKeys.contains(ski)) {
                        // first time this file descriptor has been encountered on this
                        // update?
                        if (me.updateCount != updateCount) {
                            if (ski.channel.translateAndSetReadyOps(rOps, ski)) {
                                numKeysUpdated++;
                                me.updateCount = updateCount;
                            }
                        } else {
                            // ready ops have already been set on this update
                            ski.channel.translateAndUpdateReadyOps(rOps, ski);
                        }
                    } else {
                        ski.channel.translateAndSetReadyOps(rOps, ski);
                        if ((ski.nioReadyOps() & ski.nioInterestOps()) != 0) {
                            selectedKeys.add(ski);
                            numKeysUpdated++;
                            me.updateCount = updateCount;
                        }
                    }
                }
            }
        }

        if (interrupted) {
            // Clear the wakeup pipe
            synchronized (interruptLock) {
                IOUtil.drain(fd0);
                interruptTriggered = false;
            }
        }
        return numKeysUpdated;
    }