日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java NIO学习系列三:Selector

發布時間:2025/3/21 java 59 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java NIO学习系列三:Selector 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前面的兩篇文章中總結了Java NIO中的兩大基礎組件Buffer和Channel的相關知識點,在NIO中都是通過Channel和Buffer的協作來讀寫數據的,在這個基礎上通過selector來協調多個channel以同時讀寫數據,本文我們就來學習一下selector。

  Java NIO中引入了"selector"的概念,一個selector其實是一個Java對象,能夠通過諸如連接打開、數據就緒等事件監控多個channel。如此在單個線程中就可以通過一個selector同時處理多個channel,同樣也可以同時處理多個網絡連接。

  本文會圍繞如下幾個方面展開:

  為什么要有selector

  創建selector及注冊channel

  SelectionKey

  從Selector中選擇Channels  

  Selector的一些其他操作

  總結

?

1.?為什么要有selector

  利用單個線程處理多個channel的好處是可以減少線程的數量,節省開銷。實際上,可以只用一個線程處理所有的channels。因為線程間的切換是很耗費操作系統資源的一項操作,并且每個線程都要占用一定操作系統資源(內存)。因此,線程數量當然是越少越好,而通過引入selector的概念可以顯著減少線程的數量,同時又不會減少系統處理的連接數,可以簡單理解為吞吐量。

  如下示例解釋了一個Selector處理3個Channel:

2.?創建selector及注冊channel

  通過調用Selector的靜態方法open()來創建一個selector對象:

Selector selector = Selector.open();

  要讓Selector處理Channel就需要先將Channel注冊到Selector中,可以通過調用SelectableChannel.register()方法完成:

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

  Channel必須處于非阻塞模式下才能被Selector所使用,因此FileChannel不能為Selector所用,因為它不能切換到非阻塞模式。Socket channels則支持這種工作模式,通過channel的configureBlocking()方法設置其工作模式是阻塞還是非阻塞式。

  register()的第二個參數是一個set集合(interest set),用來指代那些Selector有興趣監聽的事件。一共有四種事件類型:

  • Connect
  • Accept
  • Read
  • Write

  一個channel觸發了某一事件其實是代表著它的某些狀態已經就緒,四種事件類型分別對應如下四種情況:

  • 一個channel成功和服務器連接上就是"connect ready",由SelectionKey.OP_CONNECT指代;
  • 一個server socket channel接受了一個連接就稱為"accept ready",由SelectionKey.OP_ACCEPT指代;
  • 一個channel中數據準備好了被讀就是"read ready",由SelectionKey.OP_READ指代;
  • 一個channel可供寫入數據就是"write ready",由SelectionKey.OP_WRITE指代;

?  通過這種傳參的方式,我們可以指定selector監聽channel哪些事件,如果需要同時表示多種事件,則可以如下方式來表示:

int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

?

3.?SelectionKey

  如上一部分所示,當往Selector中注冊一個Channel時,register()方法會返回一個SelectionKey對象,其包含了如下屬性:

  • The interest set
  • The ready set
  • The Channel
  • The Selector
  • An attached object (optional)

3.1 Interest Set

  可以通過如下方法讀寫Interest Set:

int interestSet = selectionKey.interestOps(); boolean isInterestedInAccept = interestSet & SelectionKey.OP_ACCEPT; boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT; boolean isInterestedInRead = interestSet & SelectionKey.OP_READ; boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;

  可以看到,采用的是與的方式來判斷一個指定的event是否在interestSet中

3.2 Ready Set

  這個指代channel就緒的操作的集合。在selection之后就能夠獲得一個ready set(這個selection稍后會介紹到),可以通過如下方式獲取:

int readySet = selectionKey.readyOps();

  可以通過如下方式判斷是否就緒:

selectionKey.isAcceptable(); selectionKey.isConnectable(); selectionKey.isReadable(); selectionKey.isWritable();

3.3 Channel + Selector

  通過如下方式來獲取channel和selector:

Channel channel = selectionKey.channel(); Selector selector = selectionKey.selector();

3.4 Attaching Objects

  可以給SelectionKey附帶對象,這是一個手動標記一個channel的方式,或者是給channel附帶更多信息的方式。你可以附帶和channel連接的Buffer或者別的對象,使用方式如下:

// 可以這樣搞 selectionKey.attach(theObject); Object attachedObj = selectionKey.attachment();// 也可以這樣搞 SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);

?

4.?從Selector中選擇Channels

  當你往Selector中注冊了多個channel時,你可以調用select()方法用以獲取感興趣且就緒的channel,該方法有如下三種重載格式:

int select() int select(long timeout) int selectNow()
  • select()會一直阻塞,直到有一個channel就緒;
  • select(long timeout)只會阻塞一段指定的時間(單位為ms),直到有channel就緒;
  • selectNow()不會阻塞,不管是否有channel就緒都會立即返回;

  返回的int值指代有多少channel就緒(是從上一次調用select()之后開始算起)。比如調用select(),返回1,再次調用select(),這時又有一個channel就緒,此時任然是返回1。

4.1 selectedKeys()

  當調用了一次select()方法并且返回一個int值,這時你可以通過"selected key set"來獲取這些就緒的channels:

Set<SelectionKey> selectedKeys = selector.selectedKeys();

  通過調用selectedKeys()方法,返回的是一個Set,你可以遍歷以獲取就緒的channel:

Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> keyIterator = selectedKeys.iterator(); while(keyIterator.hasNext()) { SelectionKey key = keyIterator.next();if(key.isAcceptable()) {// a connection was accepted by a ServerSocketChannel.} else if (key.isConnectable()) {// a connection was established with a remote server.} else if (key.isReadable()) {// a channel is ready for reading} else if (key.isWritable()) {// a channel is ready for writing}keyIterator.remove(); }

?  通過判斷其對應的事件類型來做對應的操作。

?

5.?Selector的一些其他操作

5.1 wakeUp()

  線程調用Selector的select()方法之后會阻塞,在這種情況下可以通過另一個線程調用同一個Selector的wakeup()方法來將其喚醒。如果一個線程調用了Selector的wakeup方法但是當前并沒有線程阻塞,則下一個調用Selector的select()方法的線程則不會阻塞(還記得不,前面講到select()方法會一直阻塞)。

5.2 close()

  當使用完了Selector,需要調用其close()方法來釋放資源,該方法會關閉Selector并使所有相關的SelectionKey失效,但是和Selector相關的channel并不會被關閉。

5.3 一個例子

  開啟一個Selector,往其中注冊一個channel,并且一直監控:

Selector selector = Selector.open(); channel.configureBlocking(false); SelectionKey key = channel.register(selector, SelectionKey.OP_READ); while(true) {int readyChannels = selector.selectNow();if(readyChannels == 0) continue;Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = selectedKeys.iterator();while(keyIterator.hasNext()) {SelectionKey key = keyIterator.next();if(key.isAcceptable()) {// a connection was accepted by a ServerSocketChannel.} else if (key.isConnectable()) {// a connection was established with a remote server.} else if (key.isReadable()) {// a channel is ready for reading} else if (key.isWritable()) {// a channel is ready for writing}keyIterator.remove();} }

?

6.?總結

  本文簡單總結了Java NIO中的Selector,有了Selector,我們可以實現多路復用,通過少量的線程來監控大量的連接,實現更高性能的服務器,很多現代服務器中都采用了這一特性,比如Tomcat、netty。

  我們可以往Selector中注冊一些Channel,并且指定我們需要監聽的事件類型,然后監控這些channel,一旦獲取到就緒的事件,則可以執行下一部的操作,這就是一個Selector處理channel的基本流程。

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的Java NIO学习系列三:Selector的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。