Selector 作用
- Selector (選擇器)用于檢測一到多個 NIO Channel(通道)糙申,監(jiān)聽通道的事件口猜,這樣一個線程就可以管理多個通道草描,從而管理多個網(wǎng)絡(luò)連接
- 線程處理多個 Channel 好處是只需要更少的線程來處理通道战虏,線程之間上下切話開銷很大距贷,且每個線程要占用一些系統(tǒng)資源
Selector 使用
- 創(chuàng)建 Selector
Selector selector = Selector.open();
- 向 Selector 注冊通道 ,通道必須處于非阻塞模式(通過configureBlocking配置)相叁,這意味著不支持 FileChannel
channel.configureBlocking(false);
SelectionKey key = channel.register(selector,Selectionkey.OP_READ);
- register() 方法的第二個參數(shù)用于指定 Selector 監(jiān)聽 Channel 的哪些事件遵绰,多個事件用 | 位運算得到事件集合的值,共有以下四種事件:
- Connect(SelectionKey.OP_CONNECT)某個 Channel 連接到另一個服務(wù)器稱為“連接就緒”
- Accept(SelectionKey.OP_ACCEPT):ServerSocketChannel 準(zhǔn)備接收新進入的連接稱為“接收就緒”
- Read(SelectionKey.OP_READ):一個有數(shù)據(jù)可讀的通道為“讀就緒”
- Write(SelectionKey.OP_WRITE):等待寫入的數(shù)據(jù)通道為“寫就緒”
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
SelectionKey
-
當(dāng)向 Selector 注冊 Channel 時會返回一個 SelectionKey 對象增淹,它包含了一些屬性方法
- interest 集合:該屬性包含了我們要求 Selector 監(jiān)聽該 Channel 的事件
int interestSet = selectionKey.interestOps(); boolean isInterestedInAccept = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT椿访;
- ready 集合:表示已經(jīng)準(zhǔn)備就緒的操作的集合,用來檢測 Channel 中什么事件已經(jīng)就緒
// 可以通過位運算得出的結(jié)果值來判斷準(zhǔn)備就緒的事件集合 int readySet = selectionKey.readyOps(); // 也可以采用下面方法 selectionKey.isAcceptable(); selectionKey.isConnectable(); selectionKey.isReadable(); selectionKey.isWritable();
- channel 與 selector:返回 Channel 對象與 Selector 對象
Channel channel = selectionKey.channel(); Selector selector = selectionKey.selector();
- 附件對象:可以將一個對象或更多信息附加到 SelectionKey 上虑润,方便識別通道成玫,通過 attach 方法附加,通過 attachment 獲取附加對象
selectionKey.attach(theObject); Object attachedObj = selectionKey.attachment();
Select 方法
- select() 會返回要求 Selector 監(jiān)聽其通道的某些事件端辱,且事件已準(zhǔn)備就緒的通道
- select():阻塞到至少有一個通道的事件準(zhǔn)備就緒
- select(long timeout):指定最長會阻塞多少毫秒
- selectNow():不會阻塞梁剔,不管什么通道就緒都立刻返回(如果在前一次 select() 操作后,沒有通道變成可選擇的舞蔽,則此方法直接返回零)
- 返回的 int 值表示有多少通道已經(jīng)就緒荣病。即上次調(diào)用 select() 方法后有多少通道變成就緒狀態(tài)
SelectedKeys
- 當(dāng)調(diào)用了 select() 方法后返回值表明有一個或更多個通道就緒時,就可以調(diào)用 selectedKeys() 方法渗柿,獲取已就緒的通道
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
- 遍歷就緒的通道个盆,注意你須在處理完通道時清空 SelectionKey 集合,當(dāng)下次該通道變成就緒時朵栖,Selector 才會再次將其放入 SelectionKey 中
selectionKeys.forEach(selectionKey -> {
if(selectionKey.isAcceptable()) {
} else if (selectionKey.isConnectable()) {
} else if (selectionKey.isReadable()) {
} else if (selectionKey.isWritable()) {
}
});
selectionKeys.clear(); // 清除處理過的事件
其它方法
- wakeUp():當(dāng)調(diào)用 select() 后即使沒有通道已經(jīng)就緒颊亮,線程處于阻塞中,只要讓其它線程調(diào)用該對象的 wakeup() 方法即可讓 select() 立馬返回停止阻塞
- close():close() 會關(guān)閉 Selector陨溅,且使注冊到該 Selector 上的所有 SelectionKey 實例無效终惑,但通道本身并不會關(guān)閉