- 註冊,就是將當前
Channel
註冊到Selector
上,是NIO源碼的核心部分
1,Channel.register()部分
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT)
:註冊事件
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();
// 從當前Channel自身的註冊列表中獲取數據
SelectionKey k = findKey(sel);
// 獲取到數據後,只對SelectionKey狀態進行變更
if (k != null) {
k.interestOps(ops);
k.attach(att);
}
// key爲空,說明該任務爲新任務,加鎖後註冊
if (k == null) {
synchronized (keyLock) {
if (!isOpen())
throw new ClosedChannelException();
k = ((AbstractSelector)sel).register(this, ops, att);
// 註冊完成後,將新註冊的SelectionKey添加的Channel的註冊列表中
addKey(k);
}
}
return k;
}
}
AbstractSelectableChannel.findKey()
:從Channel
的SelectionKey[]
數組中獲取存在數據
private SelectionKey findKey(Selector sel) {
synchronized (keyLock) {
if (keys == null)
return null;
for (int i = 0; i < keys.length; i++)
// 一個Channel跟一個Selector是強綁定的,可以通過Selector匹配獲取註冊的Key
// Channel和Selector之間是多對多的關係
if ((keys[i] != null) && (keys[i].selector() == sel))
return keys[i];
return null;
}
}
- 如果 Channel 的註冊列表中不存在該數據,則直接轉發到 Selector 進行該 Channel 註冊
AbstractSelectableChannel.addKey()
:註冊完成後,添加到 Channel 的註冊列表中
private void addKey(SelectionKey k) {
assert Thread.holdsLock(keyLock);
int i = 0;
// 首先數據已經初始化且未滿, 正常填充
if ((keys != null) && (keyCount < keys.length)) {
for (i = 0; i < keys.length; i++)
if (keys[i] == null)
break;
// 數組未初始化,直接初始化,並制定默認長度3
} else if (keys == null) {
keys = new SelectionKey[3];
// 數組已經初始化,且數據已滿,
// 進行二倍擴容和原始數據遷移
} else {
// Grow key array
int n = keys.length * 2;
SelectionKey[] ks = new SelectionKey[n];
for (i = 0; i < keys.length; i++)
ks[i] = keys[i];
keys = ks;
i = keyCount;
}
// 獲取到可以填充的下標,進行數據填充
keys[i] = k;
keyCount++;
}
2,Selector.register()部分
((AbstractSelector)sel).register(this, ops, att)
:通道未註冊到選擇器,進行註冊
protected final SelectionKey register(AbstractSelectableChannel var1, int var2, Object var3) {
if(!(var1 instanceof SelChImpl)) {
throw new IllegalSelectorException();
} else {
// 初始化一個註冊器,封裝通道和選擇器
// 生成的對象真是實例爲SelectionKeyImpl
SelectionKeyImpl var4 = new SelectionKeyImpl((SelChImpl)var1, this);
// 附加各位屬性, 會透傳到下一個事件
var4.attach(var3);
Set var5 = this.publicKeys;
// 對註冊的SelectionKey列表進行同步處理
synchronized(this.publicKeys) {
// 初始化完成後,進行最終註冊,添加Socket句柄
this.implRegister(var4);
}
// 添加事件類型, 即修改原事件狀態
var4.interestOps(var2);
return var4;
}
}
SelectionKeyImpl(SelChImpl var1, SelectorImpl var2) {
this.channel = var1;
this.selector = var2;
}
WindowsSelectorImpl.implRegister
:註冊
// 註冊
protected void implRegister(SelectionKeyImpl var1) {
Object var2 = this.closeLock;
synchronized(this.closeLock) {
if(this.pollWrapper == null) {
throw new ClosedSelectorException();
} else {
// 對SelectionKeyImpl[] channelArray數組進行二倍擴容, 默認長度爲8
// 沒增加1024個Channel,則增加一個線程處理
this.growIfNeeded();
// 填充到數據的下一個索引位置
this.channelArray[this.totalChannels] = var1;
// 設置SelectionKey的索引值, 添加事件時候會用到
var1.setIndex(this.totalChannels);
// SelectionKey句柄和對象的映射
this.fdMap.put(var1);
// 添加到選擇器的註冊列表中
this.keys.add(var1);
// 添加Socket句柄到pollWrapper
// 註冊此處傳遞的總數,也就代表當前註冊對象索引
this.pollWrapper.addEntry(this.totalChannels, var1);
// 表示已註冊的Channel總數
++this.totalChannels;
}
}
}
WindowsSelectorImpl.growIfNeeded
:擴容
private void growIfNeeded() {
if(this.channelArray.length == this.totalChannels) {
// 數量超過定長,直接進行二倍擴容
int var1 = this.totalChannels * 2;
SelectionKeyImpl[] var2 = new SelectionKeyImpl[var1];
System.arraycopy(this.channelArray, 1, var2, 1, this.totalChannels - 1);
this.channelArray = var2;
this.pollWrapper.grow(var1);
}
// 數量爲1024的倍數,對線程加1,select()時作爲子線程進行查找事件
if(this.totalChannels % 1024 == 0) {
this.pollWrapper.addWakeupSocket(this.wakeupSourceFd, this.totalChannels);
++this.totalChannels;
++this.threadsCount;
}
}
PollArrayWrapper.addEntry()
:添加註冊對象的Socket句柄到內存對象中
void addEntry(int var1, SelectionKeyImpl var2) {
this.putDescriptor(var1, var2.channel.getFDVal());
}
void putDescriptor(int var1, int var2) {
this.pollArray.putInt(SIZE_POLLFD * var1 + 0, var2);
}
SelectionKeyImpl.interestOps
:修改註冊事件
public SelectionKey nioInterestOps(int var1) {
if((var1 & ~this.channel().validOps()) != 0) {
throw new IllegalArgumentException();
} else {
// 進行註冊事件修改
// 註冊事件修改,主要分析ServerSocketChannel和SocketChannel兩部分
this.channel.translateAndSetInterestOps(var1, this);
this.interestOps = var1;
return this;
}
}
// ServerSocketChannel
public void translateAndSetInterestOps(int var1, SelectionKeyImpl var2) {
int var3 = 0;
if((var1 & 16) != 0) { // 16 表示accept事件
var3 |= Net.POLLIN;
}
// 對事件進行替換
var2.selector.putEventOps(var2, var3);
}
// SocketChannel
public void translateAndSetInterestOps(int var1, SelectionKeyImpl var2) {
int var3 = 0;
if((var1 & 1) != 0) { // 讀事件
var3 |= Net.POLLIN;
}
if((var1 & 4) != 0) { // 寫事件
var3 |= Net.POLLOUT;
}
if((var1 & 8) != 0) { // 連接事件
var3 |= Net.POLLCONN;
}
// 對事件類型進行替換
var2.selector.putEventOps(var2, var3);
}
WindowsSelectorImpl.putEventOps
:事件替換
public void putEventOps(SelectionKeyImpl var1, int var2) {
Object var3 = this.closeLock;
synchronized(this.closeLock) {
if(this.pollWrapper == null) {
throw new ClosedSelectorException();
} else {
// 從註冊對象中獲取註冊事件的索引位置
int var4 = var1.getIndex();
if(var4 == -1) {
throw new CancelledKeyException();
} else {
// 通過內存對象直接操作地址空間,進行事件類型替換
this.pollWrapper.putEventOps(var4, var2);
}
}
}
}
- 註冊源碼分析完成,最終註冊事件添加到 SelectorImpl.keys 和 AbstractSelectableChannel.keys,並在 AllocatedNativeObject 內存對象中存儲,可以通過 select() 進行查找