源码阅读:理解反应器模式在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都由其创建。