NIO-selector原理

1. 概述

??Selector允許單線程處理多個 Channel。如果你的應用打開了多個連接(通道),但每個連接的流量都很低,使用Selector就會很方便。例如,在一個聊天服務器中。
??要使用Selector,得向Selector注冊Channel,然后調用它的select()方法。這個方法會一直阻塞到某個注冊的通道有事件就緒。一旦這個方法返回,線程就可以處理這些事件,事件的例子有如新連接進來,數據接收等。

2.Selector

??Selector是NIO中實現I/O多路復用的關鍵類。Selector實現了通過一個線程管理多個Channel,從而管理多個網絡連接的目的。
??僅用單個線程來處理多個Channels的好處是,只需要更少的線程來處理通道。事實上,可以只用一個線程處理所有的通道。對于操作系統來說,線程之間上下文切換的開銷很大,而且每個線程都要占用系統的一些資源(如內存)。因此,使用的線程越少越好。
??Selector能夠在單個線程中處理多個通道,這樣可以減少多個線程造成上下文切換問題。
??Channel代表這一個網絡連接通道,我們可以將Channel注冊到Selector中以實現Selector對其的管理。一個Channel可以注冊到多個不同的Selector中。

??當Channel注冊到Selector后會返回一個SelectionKey對象,該SelectionKey對象則代表這這個Channel和它注冊的Selector間的關系。并且SelectionKey中維護著兩個很重要的屬性:interestOps、readyOps
??interestOps是我們希望Selector監聽Channel的哪些事件。我們將我們感興趣的事件設置到該字段,這樣在selection操作時,當發現該Channel有我們所感興趣的事件發生時,就會將我們感興趣的事件再設置到readyOps中,這樣我們就能得知是哪些事件發生了以做相應處理。

??想想一個場景:在一個養雞場,有這么一個人,每天的工作就是不停檢查幾個特殊的雞籠,如果有雞進來,有雞出去,有雞生蛋,有雞生病等等,就把相應的情況記錄下來,如果雞場的負責人想知道情況,只需要詢問那個人即可。

??在這里,這個人就相當Selector,每個雞籠相當于一個SocketChannel,每個線程通過一個Selector可以管理多個SocketChannel

image

??為了實現Selector管理多個SocketChannel,必須將具體的SocketChannel對象注冊到Selector,并聲明需要監聽的事件(這樣Selector才知道需要記錄什么數據),一共有4種事件:

1、connect:客戶端連接服務端事件,對應值為SelectionKey.OP_CONNECT(8) 2、accept:服務端接收客戶端連接事件,對應值為SelectionKey.OP_ACCEPT(16) 3、read:讀事件,對應值為SelectionKey.OP_READ(1) 4、write:寫事件,對應值為SelectionKey.OP_WRITE(4)

??每次請求到達服務器,都是從connect開始,connect成功后,服務端開始準備accept,準備就緒,開始讀數據,并處理,最后寫回數據返回。

image.png

過程:
1、創建ServerSocketChannel實例,并綁定制定端口; 2、創建Selector實例; 3、將serverSocketChannel注冊到Selector,并指定事件OP_ACCEPT,最底層的socket通過channel和selector建立關聯; 4、如果沒有準備號的socket,select方法會被阻塞一段時間并返回0; 5、如果底層有socket已經準備好,selector的select方法會返回socket的個數,而且selectedKeys方法會返回socket對應的事件(connect、accept、read or write); 6、根據時間類型,進行不同的處理邏輯。

在步驟3中,selector只注冊了serverSocketChannel的OP_ACCEPT事件
1、如果有客戶端A連接服務,執行select方法時,可以通過serverSocketChannel獲取客戶端A的socketChannel,并在selector上注冊socketChannel的OP_READ事件。
2、如果客戶端A發送數據,會觸發read事件,這樣下次輪詢調用select方法時,就能通過socketChannel讀取數據,同時在selector上注冊該socketChannel的OP_WRITE事件,實現服務器往客戶端寫數據

3.Selector(選擇器)的使用方法詳細描述

image

??NIO中實現非阻塞IO的核心設計Selector,Selector就是注冊各種IO事件的地方,而且當那些事件發生時,就是這個對象告訴我們所發生的事件。
??當有讀或者寫等任何注冊的事件發生時,可以從Selector中獲得相應的SelectionKey,同時從SelectionKey中可以找到發生的事件和該事件所發生的具體的SelectableChannel,以獲得客戶端發送過來的數據。
使用NIO中非阻塞IO編寫服務器處理程序,有三個步驟
1.向Selector對象注冊感興趣的事件 2.從Selector中獲取感興趣的事件 3.根據不同事件進行相應的處理

1) Selector的創建

??通過調用Selector.open()方法創建一個Selector對象,如下:
??Selector selector = Selector.open();

2)注冊Channel到Selector

channel.configureBlocking(false); SelectionKey key = channel.register(selector, Selectionkey.OP_READ);

Channel必須是非阻塞的

??所以FileChannel不適用Selector,因為FileChannel不能切換為非阻塞模式,更準確的來說是因為FileChannel沒有繼承SelectableChannelSocket channel可以正常使用。

SelectableChannel抽象類 有一個 configureBlocking() 方法用于使通道處于阻塞模式或非阻塞模式。

abstract SelectableChannel configureBlocking(boolean block)

3)SelectionKey介紹

??一個SelectionKey鍵表示了一個特定的通道對象和一個特定的選擇器對象之間的注冊關系。
key.attachment(); //返回SelectionKey的attachment,attachment可以在注冊channel的時候指定。
key.channel(); // 返回該SelectionKey對應的channel。
key.selector(); // 返回該SelectionKey對應的Selector。
key.interestOps(); //返回代表需要Selector監控的IO操作的bit mask
key.readyOps(); // 返回一個bit mask,代表在相應channel上可以進行的IO操作。

4)從Selector中選擇channel(Selecting Channels via a Selector)

??選擇器維護注冊過的通道的集合,并且這種注冊關系都被封裝在SelectionKey當中.
??Selector維護的三種類型SelectionKey集合:

  • 已注冊的鍵的集合(Registered key set)

  • 已選擇的鍵的集合(Selected key set)
    所有與選擇器關聯的通道所生成的鍵的集合稱為已經注冊的鍵的集合。并不是所有注冊過的鍵都仍然有效。這個集合通過 keys() 方法返回,并且可能是空的。這個已注冊的鍵的集合不是可以直接修改的;試圖這么做的話將引發java.lang.UnsupportedOperationException。

  • 已取消的鍵的集合(Cancelled key set)

5)select()方法介紹:

??在剛初始化的Selector對象中,這三個集合都是空的。 通過Selector的select()方法可以選擇已經準備就緒的通道 (這些通道包含你感興趣的的事件)。比如你對讀就緒的通道感興趣,那么select()方法就會返回讀事件已經就緒的那些通道。下面是Selector幾個重載的select()方法:

??select()
??獲取就緒的 Channel,阻塞方法,沒有就緒的 Channel 就一直阻塞該線程。

??select(long timeout)
??獲取就緒的 Channel, 阻塞方法,阻塞 timeout 時間,如果超時還沒有就緒的 Channel,返回0,不做任何操作。
??selectNow()
??獲取就緒的 Channel,如果沒有就緒的就直接返回,不阻塞當前線程。

上面三個 select方法底層都是調用 lockAndDoSelect 方法。 lockAndDoSelect方法的參數值 說明: -1 : 一直阻塞,直到有就緒的 Channel 可處理 0 : 不阻塞 0: 表示阻塞多長時間

??select()方法返回的int值表示有多少通道已經就緒,是自上次調用select()方法后有多少通道變成就緒狀態。
??之前在select()調用時進入就緒的通道不會在本次調用中被記入,而在前一次select()調用進入就緒但現在已經不在處于就緒的通道也不會被記入。
??例如:首次調用select()方法,如果有一個通道變成就緒狀態,返回了1,若再次調用select()方法,如果另一個通道就緒了,它會再次返回1。如果對第一個就緒的channel沒有做任何操作,現在就有兩個就緒的通道,但在每次select()方法調用之間,只有一個通道就緒了。
??一旦調用select()方法,并且返回值不為0時,則 可以通過調用SelectorselectedKeys()方法來訪問已選擇鍵集合 。如下:
Set selectedKeys=selector.selectedKeys();
進而可以放到和某SelectionKey關聯的SelectorChannel

歡迎關注公眾號


image.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,748評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,165評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,595評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,633評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,435評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,943評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,035評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,175評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,713評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,599評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,788評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,303評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,034評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,412評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,664評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,408評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,747評論 2 370

推薦閱讀更多精彩內容

  • # Java NIO # Java NIO屬于非阻塞IO,這是與傳統IO最本質的區別。傳統IO包括socket和文...
    Teddy_b閱讀 601評論 0 0
  • Java NIO(New IO)是從Java 1.4版本開始引入的一個新的IO API,可以替代標準的Java I...
    JackChen1024閱讀 7,562評論 1 143
  • 在本章中,我們將探索選擇器(selectors)。選擇器提供選擇執行已經就緒的任務的能力,這使得多元 I/O 成為...
    沉淪2014閱讀 661評論 0 2
  • Android網絡編程 目錄 1、Java NIO 介紹 NIO是java New IO的簡稱,在jdk1.4里提...
    香沙小熊閱讀 4,773評論 0 6
  • 背景 java.nio全稱java non-blocking IO,是指jdk1.4 及以上版本里提供的新api(...
    ninetyhe_閱讀 425評論 0 0