前言:在Socket通信中使用Select 来对NIO 进行实现,那么它们的实现方式是怎样的呢,本文从 Selector.open() 进行第一步的分析;
Selector.open() :
Selector 类:
public static Selector open() throws IOException {
// 通过 SelectorProvider.provider() 获取SelectorProvider实例
// 通过openSelector() 获取不同系统的Selector 实现
return SelectorProvider.provider().openSelector();
}
先看:SelectorProvider.provider():
SelectorProvider 类:
private static final Object lock = new Object();
private static SelectorProvider provider = null;
public static SelectorProvider provider() {
synchronized (lock) {
// 通过 lock 对象锁,保证当前进程只会有一个SelectorProvider 对象
if (provider != null)// 如果发现进程中已经实例化过SelectorProvider 对象则直接返回
return provider;
// 进程下没有过SelectorProvider 对象则进行加载
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;
}
});
}
}
SelectorProvider.provider()方法会在System Property中不存在java.nio.channels.spi.SelectorProvider属性和不能找到SelectorProvider的实现类时,创建一个默认的sun.nio.ch.DefaultSelectorProvider来作为SelectorProvide;DefaultSelectorProvider 会根据不同的操作系统返回:
window 下的DefaultSelectorProvider:
package sun.nio.ch;
import java.nio.channels.spi.SelectorProvider;
public class DefaultSelectorProvider {
private DefaultSelectorProvider() {
}
public static SelectorProvider create() {
return new WindowsSelectorProvider();
}
}
DefaultSelectorProvider.create() 创建方法:可以看到返回了WindowsSelectorProvider一个实例对象;
package sun.nio.ch;
import java.io.IOException;
import java.nio.channels.spi.AbstractSelector;
public class WindowsSelectorProvider extends SelectorProviderImpl {
public WindowsSelectorProvider() {
}
public AbstractSelector openSelector() throws IOException {
return new WindowsSelectorImpl(this);
}
}
在回到Selector 中的 SelectorProvider.provider().openSelector(), SelectorProvider.provider() 实际上返回了WindowsSelectorProvider一个实例对象,然后调用WindowsSelectorProvider的openSelector()方法,可以看到这里创建了一个WindowsSelectorImpl 实例对象并进行了返回:
这里看下对象的关系:
1 ) WindowsSelectorImpl extends SelectorImpl;
2) SelectorImpl extends AbstractSelector;
3) AbstractSelector extends Selector
4) Selector implements Closeable
WindowsSelectorImpl 类:
//poll数组和channel数组的初始容量
private final int INIT_CAP = 8;
//select操作时,每个线程处理的最大FD数量。为INIT_CAP乘以2的幂
private final static int MAX_SELECTABLE_FDS = 1024;
//由这个选择器服务的SelectableChannel的列表
private SelectionKeyImpl[] channelArray = new SelectionKeyImpl[INIT_CAP];
//存放所有FD的包装器,主要用于poll操作
private PollArrayWrapper pollWrapper;
//注册到当前选择器上总的通道数量,初始化为1是因为实例化选择器时加入了wakeupSourceFd
private int totalChannels = 1;
//选择操作所需要的辅助线程数量。每增加一组MAX_SELECTABLE_FDS - 1个通道,就需要一个线程。
private int threadsCount = 0;
//辅助线程列表
private final List<SelectThread> threads = new ArrayList();
//创建一个Pipe实例,用于实现唤醒选择器的功能
private final Pipe wakeupPipe ;
//管道的read端FD,用于实现唤醒选择器的功能
private final int wakeupSourceFd;
//管道的write端FD,用于实现唤醒选择器的功能
private final int wakeupSinkFd;
//关闭锁,通常在注册、注销,关闭,修改选择键的interestOps时都存在竞态条件,主要保护channelArray、pollWrapper等
private Object closeLock = new Object();
//FD为键,SelectionKeyImpl为value的内部map,方便通过FD查找SelectionKeyImpl
private final FdMap fdMap = new FdMap();
//内部类SubSelector中封装了发起poll调用和处理poll调用结果的细节。由主线程调用
private final SubSelector subSelector = new SubSelector();
//选择器每次选择的超时参数
private long timeout;
//中断锁,用于保护唤醒选择器使用的相关竞态资源,如interruptTriggered
private final Object interruptLock = new Object();
//是否触发中断,唤醒选择器的重要标志,由interruptLock保护
private volatile boolean interruptTriggered = false;
//启动锁,当使用多线程处理选择器上Channel的就绪事件时,用于协调这些线程向内核发起系统调用
//辅助线程会在该锁上等待
private final WindowsSelectorImpl.StartLock startLock = new WindowsSelectorImpl.StartLock();
//完成锁,当使用多线程处理选择器上Channel的就绪事件时,用于协调这些线程从系统调用中返回
//主线程会在该锁上等待
private final WindowsSelectorImpl.FinishLock finishLock = new WindowsSelectorImpl.FinishLock();
//updateSelectedKeys调用计数器
//SubSelector.fdsMap中的每个条目都有一个的updateCount值。调用processFDSet时,当我们增加numKeysUpdated,
//会同步将updateCount设置为当前值。 这用于避免多次计算同一个选择键更新多次numKeysUpdated。
//同一个选择键可能出现在readfds和writefds中。
private long updateCount = 0L;
WindowsSelectorImpl(SelectorProvider var1) throws IOException {
// 调用 SelectorImpl 的父类构造方法
// 将 上一步WindowsSelectorProvider的openSelector() SelectorProvider 实例传入
super(var1);
// Pipe wakeupPipe = Pipe.open() 的fd 文件描述符赋值
this.wakeupSourceFd = ((SelChImpl)this.wakeupPipe.source()).getFDVal();
SinkChannelImpl var2 = (SinkChannelImpl)this.wakeupPipe.sink();
// 禁用 Nagle 算法,当 sink 端写入 1 字节数据时,将立即发送,而不必等到将较小的包组合成较大的包再发送,
// 这样 source 端就可以立马读取数据
var2.sc.socket().setTcpNoDelay(true);
// Pipe wakeupPipe = Pipe.open() 的fd 文件描述符赋值
this.wakeupSinkFd = var2.getFDVal();
// Pipe wakeupPipe = Pipe.open() 的fd 文件描述符赋值加入到管道数组中
// 把唤醒端的文件描述符(wakeupSourceFd)放到pollWrapper里
this.pollWrapper.addWakeupSocket(this.wakeupSourceFd, 0);
}
WindowsSelectorImpl 类中通过socket 建立了管道连接,并将管道符进行保存;
super(var1) 调用 SelectorImpl 的父类构造方法:
protected Set<SelectionKey> selectedKeys = new HashSet();
protected HashSet<SelectionKey> keys = new HashSet();
private Set<SelectionKey> publicKeys;
private Set<SelectionKey> publicSelectedKeys;
protected SelectorImpl(SelectorProvider var1) {
// 调用 AbstractSelector 的父类构造方法
super(var1);
// publicKeys 和 publicSelectedKeys 进行映射赋值
if (Util.atBugLevel("1.4")) {
this.publicKeys = this.keys;
this.publicSelectedKeys = this.selectedKeys;
} else {
this.publicKeys = Collections.unmodifiableSet(this.keys);
this.publicSelectedKeys = Util.ungrowableSet(this.selectedKeys);
}
}
SelectorImpl 的构造,对publicKeys和publicSelectedKeys 集合做了映射,方便操作selectedKeys和keys 集合可以直接影响数据;
super(var1) 调用 AbstractSelector的父类构造方法:
private final SelectorProvider provider;
protected AbstractSelector(SelectorProvider provider) {
this.provider = provider;
}
因为WindowsSelectorImpl 的实例处理构造方法的调用就是对Pipe wakeupPipe = Pipe.open() 产生的文件操作符进行赋值,这里看下 Pipe.open():
Pipe 类:
public static Pipe open() throws IOException {
// SelectorProvider.provider() 返回直接创建好的WindowsSelectorProvider的openSelector() SelectorProvider 实例传入
// 然后调用openPipe()
return SelectorProvider.provider().openPipe();
}
SelectorProviderImpl类中的openPipe():
public Pipe openPipe() throws IOException {
// 返回 PipeImpl 的实例,传入创建好的WindowsSelectorProvider的openSelector() SelectorProvider 实例
return new PipeImpl(this);
}
PipeImpl 的构造方法:
private static final int NUM_SECRET_BYTES = 16;
private static final Random RANDOM_NUMBER_GENERATOR = new SecureRandom();
private SourceChannel source;
private SinkChannel sink;
PipeImpl(SelectorProvider var1) throws IOException {
try {
// AccessController.doPrivileged() 借用权限完成方法的执行
AccessController.doPrivileged(new PipeImpl.Initializer(var1));
} catch (PrivilegedActionException var3) {
throw (IOException)var3.getCause();
}
}
重点看下PipeImpl 内部类中 new PipeImpl.Initializer(var1),在进行构造之后,执行run方法:
private class Initializer implements PrivilegedExceptionAction<Void> {
private final SelectorProvider sp;
private IOException ioe;
private Initializer(SelectorProvider var2) {
// 异常原因
this.ioe = null;
// SelectorProvider 实例
this.sp = var2;
}
public Void run() throws IOException {
// 启动线程执行LoopbackConnector 的run方法
PipeImpl.Initializer.LoopbackConnector var1 = new PipeImpl.Initializer.LoopbackConnector();
var1.run();
if (this.ioe instanceof ClosedByInterruptException) {
// 如果建立通道过程中发生了关闭异常则重新发起线程调用LoopbackConnector 的run方法
this.ioe = null;
Thread var2 = new Thread(var1) {
public void interrupt() {
}
};
var2.start();
while(true) {
try {
var2.join();
break;
} catch (InterruptedException var4) {
}
}
Thread.currentThread().interrupt();
}
if (this.ioe != null) {
// 发生异常抛出异常
throw new IOException("Unable to establish loopback connection", this.ioe);
} else {
return null;
}
}
private class LoopbackConnector implements Runnable {
private LoopbackConnector() {
}
public void run() {
// 声明服务端的 ServerSocketChannel
ServerSocketChannel var1 = null;
// 声明客户端的SocketChannel
SocketChannel var2 = null;
SocketChannel var3 = null;
try {
// 声明var4 和 var5 两个内存空间分别为16个字节大小
ByteBuffer var4 = ByteBuffer.allocate(16);
ByteBuffer var5 = ByteBuffer.allocate(16);
// 声明本机的地址
InetAddress var6 = InetAddress.getByName("127.0.0.1");
assert var6.isLoopbackAddress();
// 声明var7 地址
InetSocketAddress var7 = null;
while(true) {
if (var1 == null || !var1.isOpen()) {
// 初始化服务端的ServerSocketChannel
var1 = ServerSocketChannel.open();
// 绑定服务端监听的端口ip 和端口
var1.socket().bind(new InetSocketAddress(var6, 0));
// 将地址赋值给var7
var7 = new InetSocketAddress(var6, var1.socket().getLocalPort());
}
// 打开客户端SocketChannel ,地址为var7 建立连接
var2 = SocketChannel.open(var7);
// 向var4 写入随机数
PipeImpl.RANDOM_NUMBER_GENERATOR.nextBytes(var4.array());
do {
// 向SocketChannel 建立的连接写入数据
var2.write(var4);
} while(var4.hasRemaining());
// 返回此缓冲区
var4.rewind();
// 服务端阻塞等待连接连接
var3 = var1.accept();
do {
// 从服务端连接中读取数据
var3.read(var5);
} while(var5.hasRemaining());
// 返回此缓冲区
var5.rewind();
// 如果客户端发送的数据和服务端接收到的数据相同说通道完成建立
if (var5.equals(var4)) {
// 放入通道描述
PipeImpl.this.source = new SourceChannelImpl(Initializer.this.sp, var2);
PipeImpl.this.sink = new SinkChannelImpl(Initializer.this.sp, var3);
// 跳出循环
break;
}
// 关闭
var3.close();
var2.close();
}
} catch (IOException var18) {
try {
if (var2 != null) {
var2.close();
}
if (var3 != null) {
var3.close();
}
} catch (IOException var17) {
}
// 异常原因
Initializer.this.ioe = var18;
} finally {
try {
if (var1 != null) {
// 关闭
var1.close();
}
} catch (IOException var16) {
}
}
}
}
}
pollWrapper.addWakeupSocket(this.wakeupSourceFd, 0):
PollArrayWrapper类:
void putDescriptor(int var1, int var2) {
this.pollArray.putInt(SIZE_POLLFD * var1 + 0, var2);
}
void putEventOps(int var1, int var2) {
this.pollArray.putShort(SIZE_POLLFD * var1 + 4, (short)var2);
}
void addWakeupSocket(int var1, int var2) {
this.putDescriptor(var2, var1);
this.putEventOps(var2, Net.POLLIN);
}
这里将source的POLLIN事件标识为感兴趣的,当sink端有数据写入时,source对应的文件描述符wakeupSourceFd就会处于就绪状态;
总结:
参考:
Java NIO 之 Selector(第一部分Selector.open());