源码阅读:理解反应器模式在javaNIO中的实现-Selector2

源码阅读:理解反应器模式在javaNIO中的实现-Selector2

接上文:源码阅读:理解反应器模式在javaNIO中的实现-Selector1

本文主要看SelectionKey,以及selector的创建者selectorProvider。

SelectionKey

* A token representing the registration of a {@link SelectableChannel} with a
 * {@link Selector}.

类最上面都有一句简洁的说明,感觉很好。

一个标记了channel和selector注册关系的token。
每当一个selectable注册到selector时都会创建一个selectionKey。
一个key在调用自身的cancel()方法/channel被关闭/selector被关闭时失效。
当key被调用cancel()方法,它不会马上从selector中移除,而是会添加到canceled-key set中(上文已经讲到),直到下次selection操作时才会被移除。可以通过isValid()方法判断其是否仍有效。
类其中维护了两个set:

interest set(interest ops),其表明了channel的感兴趣事件(监听事件)

ready set,其表明了channel已就绪的事件。

ready set表明channel已对某事件就绪,但是只是暗示该感兴趣事件可以被一个线程不阻塞地执行。当某件操作已经完成,这时的ready set数据是精确的。外部的事件以及同类的channel的io操作也可能造成不精确的值。(这里不太了解,本身就是对某事件的就绪情况,为啥会在一个事件操作执行完成后获得准确的值?)

这边的operation-set,只能是对应类型的,channel中支持的interest ops(validops),在该类中修改不支持的ops会抛出runtime exception。
channel支持的ops通过channel的validops()获取。

通常来说都需要对监听到的事件绑定后续操作(handler),所以在该类中定义一个属性attachment,在监听到感兴趣事件后能调用attachment里维护的后续操作。通过attach方法绑定,并通过attachment方法获得。

selection keys是支持多线程并发的,其write/read方法是需要加同步锁的,其实现方式在其继承类中定义。

接下来看其中定义的对象:

public abstract class SelectionKey {

//构造器
protected SelectionKey() { }


// -- Channel and selector operations --

/维护的channel
public abstract SelectableChannel channel();

//维护的selector
public abstract Selector selector();

/判断是否有效
public abstract boolean isValid();

//取消该绑定
public abstract void cancel();


// -- Operation-set accessors --

//获得感兴趣事件
public abstract int interestOps();

//设置感兴趣事件
public abstract SelectionKey interestOps(int ops);

//获取就绪事件
public abstract int readyOps();


// -- Operation bits and bit-testing convenience methods --

//OP_READ的值 1
public static final int OP_READ = 1 << 0;

/OP_WRITE的值 4
public static final int OP_WRITE = 1 << 2;

/OP_CONNECT的值 8
public static final int OP_CONNECT = 1 << 3;

/OP_ACCEPT的值,16
public static final int OP_ACCEPT = 1 << 4;

//判断是否能对op_READ就绪
//判断通过和操作readyops转成2进制的第四位如果是1,则返回true。
//下面几个方法类似。
public final boolean isReadable() {
    return (readyOps() & OP_READ) != 0;
}

public final boolean isWritable() {
    return (readyOps() & OP_WRITE) != 0;
}


public final boolean isConnectable() {
    return (readyOps() & OP_CONNECT) != 0;
}


public final boolean isAcceptable() {
    return (readyOps() & OP_ACCEPT) != 0;
}


// -- Attachments --

private volatile Object attachment = null;

private static final AtomicReferenceFieldUpdater<SelectionKey,Object>
    attachmentUpdater = AtomicReferenceFieldUpdater.newUpdater(
        SelectionKey.class, Object.class, "attachment"
    );
    
//添加捆绑事件
public final Object attach(Object ob) {
    return attachmentUpdater.getAndSet(this, ob);
}

//获得捆绑事件
public final Object attachment() {
    return attachment;
}

}

SelectorProvider

接下来看selector的创建类selectorprovider
发现channel和selector都是通过selectorProvider.provider()创建的。

再看注解的第一句:

/**
 * Service-provider class for selectors and selectable channels.
 *

是对选择器以及可选择的通道的服务创建类。


public abstract class SelectorProvider {

    private static final Object lock = new Object();
    private static SelectorProvider provider = null;

   
    protected SelectorProvider() {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null)
            sm.checkPermission(new RuntimePermission("selectorProvider"));
    }

    private static boolean loadProviderFromProperty() {
        String cn = System.getProperty("java.nio.channels.spi.SelectorProvider");
        if (cn == null)
            return false;
        try {
            Class<?> c = Class.forName(cn, true,
                                       ClassLoader.getSystemClassLoader());
            provider = (SelectorProvider)c.newInstance();
            return true;
        } catch (ClassNotFoundException x) {
            throw new ServiceConfigurationError(null, x);
        } catch (IllegalAccessException x) {
            throw new ServiceConfigurationError(null, x);
        } catch (InstantiationException x) {
            throw new ServiceConfigurationError(null, x);
        } catch (SecurityException x) {
            throw new ServiceConfigurationError(null, x);
        }
    }

    private static boolean loadProviderAsService() {

        ServiceLoader<SelectorProvider> sl =
            ServiceLoader.load(SelectorProvider.class,
                               ClassLoader.getSystemClassLoader());
        Iterator<SelectorProvider> i = sl.iterator();
        for (;;) {
            try {
                if (!i.hasNext())
                    return false;
                provider = i.next();
                return true;
            } catch (ServiceConfigurationError sce) {
                if (sce.getCause() instanceof SecurityException) {
                    // Ignore the security exception, try the next provider
                    continue;
                }
                throw sce;
            }
        }
    }

  
    public static SelectorProvider provider() {
        synchronized (lock) {
            if (provider != null)
                return provider;
            return AccessController.doPrivileged(
                new PrivilegedAction<SelectorProvider>() {
                    public SelectorProvider run() {
                            if (loadProviderFromProperty())
                                return provider;
                            if (loadProviderAsService())
                                return provider;
                            provider = sun.nio.ch.DefaultSelectorProvider.create();
                            return provider;
                        }
                    });
        }
    }

  
    public abstract DatagramChannel openDatagramChannel()
        throws IOException;

  
    public abstract DatagramChannel openDatagramChannel(ProtocolFamily family)
        throws IOException;

   
    public abstract Pipe openPipe()
        throws IOException;

   
    public abstract AbstractSelector openSelector()
        throws IOException;

   
    public abstract ServerSocketChannel openServerSocketChannel()
        throws IOException;

   
    public abstract SocketChannel openSocketChannel()
        throws IOException;

   
   public Channel inheritedChannel() throws IOException {
        return null;
   }

}

其中几个方法是对不同对象的创建。我们主要来看以下几个方法:

  private static boolean loadProviderFromProperty() {
        String cn = System.getProperty("java.nio.channels.spi.SelectorProvider");
        if (cn == null)
            return false;
        try {
            Class<?> c = Class.forName(cn, true,
                                       ClassLoader.getSystemClassLoader());
            provider = (SelectorProvider)c.newInstance();
            return true;
        } catch (ClassNotFoundException x) {
            throw new ServiceConfigurationError(null, x);
        } catch (IllegalAccessException x) {
            throw new ServiceConfigurationError(null, x);
        } catch (InstantiationException x) {
            throw new ServiceConfigurationError(null, x);
        } catch (SecurityException x) {
            throw new ServiceConfigurationError(null, x);
        }
    }

SelectorProvider是从虚拟机中获得的属性。

  private static boolean loadProviderAsService() {

        ServiceLoader<SelectorProvider> sl =
            ServiceLoader.load(SelectorProvider.class,
                               ClassLoader.getSystemClassLoader());
        Iterator<SelectorProvider> i = sl.iterator();
        for (;;) {
            try {
                if (!i.hasNext())
                    return false;
                provider = i.next();
                return true;
            } catch (ServiceConfigurationError sce) {
                if (sce.getCause() instanceof SecurityException) {
                    // Ignore the security exception, try the next provider
                    continue;
                }
                throw sce;
            }
        }
    }

同时它还可以从类加载器中去获取。如果有多个该类的service,则拿第一个指定的

然后是关键的provider()方法:

 public static SelectorProvider provider() {
        synchronized (lock) {
            if (provider != null)
                return provider;
            return AccessController.doPrivileged(
                new PrivilegedAction<SelectorProvider>() {
                    public SelectorProvider run() {
                            if (loadProviderFromProperty())
                                return provider;
                            if (loadProviderAsService())
                                return provider;
                            provider = sun.nio.ch.DefaultSelectorProvider.create();
                            return provider;
                        }
                    });
        }
    }

首先要从虚拟机中去拿类,如果拿不到就要抛出unspecified error。
如果selectorProvider在自动装配时生成了,则会去拿拿到的第一个类。

并创建该类。拿到了该类就可以拿它去创建各种东西了,包括channel,pipe,seletor。

最后还有一个方法:

public Channel inheritedChannel() throws IOException {
        return null;
   }

返回从创建此 java 虚拟机的实体中继承的通道。
可能会是多种channel(这个方法有什么用?)
https://stackoverflow.com/questions/14180353/whats-use-of-inheritedchannel-method-in-system-class

It’s designed for Java programs to be started on demand from inetd or xinetd on Unixy systems.

是为了unix系统设计的,具体先不做了解。

总结:

selectionKey是保存了selector和channel的注册绑定关系。并在selecotr中维护了3个selectionkey set(源码阅读:理解反应器模式在javaNIO中的实现-Selector1里有说明),其还有一个attach属性,绑定了监听事件的后续操作。从而实现逻辑:创建channel,绑定到selector,通过selector的selection-key去轮询就绪的操作,并执行就绪的操作对应的后续操作。即reactor模式(第四篇文章终于回到反应器模式了):
注册 channel->selector

轮询 selector->reactor

分发 reactor->handler

除此之外,还了解了selectorprovider的生成方式,默认从虚拟机中获取类,如果在代码中定义了该类,则会优先获取自定义的类,并在provider()方法中完成创建。selector以及serversocketchannel/socketchannel都由其创建。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值