精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

聊聊Java NIO Selector 使用

開發 前端
之前的文章已經把 Java 中 NIO 的 Buffer、Channel 講解完了,不太了解的可以先回過頭去看看。這篇文章我們就來聊聊 Selector — 選擇器。

之前的文章已經把 Java 中 NIO 的 Buffer、Channel 講解完了,不太了解的可以先回過頭去看看。這篇文章我們就來聊聊 Selector —— 選擇器。

首先 Selector 是用來干嘛的呢?不熟悉這個概念的話我們其實可以這么理解:

selector

把它當作 SQL 中的 select 語句,在 SQL 中無非就是篩選出符合條件的結果集合。而 NIO 中的 Selector 用途類似,只不過它選擇出來的是有就緒 IO 事件的 Channel。

IO 事件代表了 Channel 對于不同的 IO 操作所處的不同的狀態,而不是對 Channel 進行 IO 操作。總共有 4 種 IO 事件的定義:

  • OP_READ 可讀
  • OP_WRITE 可寫
  • OP_CONNECT 連接
  • OP_ACCEPT 接收

IO 事件分類

比如 OP_READ,其就緒是指數據已經在內核態 Ready 了并且已經從內核態復制到了用戶態的緩沖區,然后我們的應用程序就可以去讀取數據了,這叫可讀。

再比如 OP_CONNECT,當某個 Channel 已經完成了握手連接,則 Channel 就會處于 OP_CONNECT 的狀態。

對用戶態和內核態不了解的,可以去看看之前寫的 《用戶態和內核態的區別》

在之前講 BIO 模型的時候說過,用戶態在發起 read 系統調用之后會一直阻塞,直到數據在內核態 Ready 并且復制到用戶態的緩沖區內。如果只有一個用戶還好,隨便你阻塞多久。但要是這時有其他用戶發請求進來了,就會一直卡在這里等待。這樣串行的處理會導致系統的效率極其低下。

針對這個問題,也是有解決方案的。那就是為每個用戶都分配一個線程(即 Connection Per Thread),乍一想這個思路可能沒問題,但使用線程需要消耗系統的資源,例如在 JVM 中一個線程會占用較多的資源,非常昂貴。系統稍微并發多一些(例如上千),你的系統就會直接 OOM 了。而且,線程頻繁的創建、銷毀、切換也是一個比較耗時的操作。

而如果用 NIO,雖然不會阻塞了,但是會一直輪詢,讓 CPU 空轉,也是一個不環保的方式。

而如果用 Selector,只需要一個線程來監聽多個 Channel,而這個多個可以上千、上萬甚至更多。那這些 Channel 是怎么跟 Selector 關聯上的呢?

答案是通過注冊,因為現在變成了 Selector 決定什么時候處理 Channel 中的事件,而注冊操作則相當于將 Channel 的控制權轉交給了 Selector。一旦注冊上了,后續當 Channel 有就緒的 IO 事件,Selector 就會將它們選擇出來執行對應的操作。

說了這么多,來看個例子吧,客戶端的代碼相對簡單,后續再看,我們先看服務端的:

public static void main(String[] args) throws IOException {
// 創建 selector, 管理多個 channel
Selector selector = Selector.open();

// 創建 ServerSocketChannel 并且綁定端口
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(8080));

// 將 channel 注冊到 selector 上
SelectionKey serverSocketChannelKey = serverSocketChannel.register(selector, 0);
// 由于總共有 4 種事件, 分別是 accept、connect、read 和 write,
// 分別代表有連接請求時觸發、客戶端建立連接時觸發、可讀事件、可寫事件
// 我們可以使用 interestOps 來表明只處理有連接請求的事件
serverSocketChannelKey.interestOps(SelectionKey.OP_ACCEPT);

System.out.printf("serverSocketChannel %s\n", serverSocketChannelKey);
while (true) {
// 沒有事件發生, 線程會阻塞; 有事件發生, 就會讓線程繼續執行
System.out.println("start to select...");
selector.select();
// 換句話說, 有連接過來了, 就會繼續往下走

// 通過 selectedKeys 包含了所有發生的事件, 可能會包含 READ 或者 WRITE
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
System.out.printf("selected key %s\n", key);

// 這里需要進行事件區分
if (key.isAcceptable()) {
System.out.println("get acceptable event");

// 觸發此次事件的 channel, 拿到事件一定要處理, 否則會進入非阻塞模式, 空轉占用 CPU
// 例如你可以使用 key.cancel()
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = channel.accept();
socketChannel.configureBlocking(false);

// 這個 socketChannel 也需要注冊到 selector 上, 相當于把控制權交給 selector
SelectionKey socketChannelKey = socketChannel.register(selector, 0);
socketChannelKey.interestOps(SelectionKey.OP_READ);
System.out.printf("get socketChannel %s\n", socketChannel);
} else if (key.isReadable()) {
System.out.println("get readable event");

SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buf = ByteBuffer.allocate(16);
channel.read(buf);
buf.flip();
ByteBufferUtil.debugRead(buf);
key.cancel();
}

iterator.remove();
}
}
}

看起來有點多,但相應的注釋都寫了,可以先看看。其實這里的很多代碼跟之前的玩轉 Channel 的代碼差不多的,這里抽一些我認為值得講的解釋一下。

首先就是 Selector.open(),跟 Channel 的 open 方法類似,可以理解為創建一個 selector。

其次就是 SelectionKey serverSocketChannelKey = serverSocketChannel.register(selector, 0); 了,我們調用了 serverSocketChannel 的注冊方法之后,返回了一個 SelectionKey,這是個什么概念呢?

說簡單點,你可以把 SelectionKey 理解為你去商場寄存柜存東西,那個機器吐給你的提取憑證

換句話說,這個 SelectionKey 就是當前這個 serverSocketChannel 注冊到 selector 上的憑證。selector 會維護一個 SelectionKey 的集合,用于統一管理。

selectionkey 集合

上圖中的每個 Key 都代表了一個具體的 Channel。

而至于 register 的第二個參數,我們傳入的是 0,代表了當前 Selector 需要關注這個 Channel 的哪些 IO 事件。0 代表不關注任何事件,我們這里是通過 serverSocketChannelKey.interestOps(SelectionKey.OP_ACCEPT); 來告訴 Selector,對這個 Channel 只關注 OP_ACCEPT 事件。

IO 事件有 4 個,如果你想要同時監聽多個 IO 事件怎么辦呢?答案是通過或運算符。

serverSocketChannelKey.interestOps(SelectionKey.OP_ACCEPT | SelectionKey.OP_READ);

上面說過,NIO 雖然不阻塞,但會一直輪詢占用 CPU 的資源,而 Selector 解決了這個問題。在調用完 selector.select(); 之后,線程會在這里阻塞,而不會像 NIO 一樣瘋狂輪詢,把 CPU 拉滿。所以 Selector 只會在有事件處理的時候才執行,其余時間都會阻塞,極大的減少了 CPU 資源的占用。

當客戶端調用 connect 發起連接之后,Channel 就會處于 OP_CONNECT 就緒狀態,selector.select(); 就不會再阻塞,會繼續往下運行,即:

Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();

其中 selectedKeys 這個名字也能看出來,表示被選出來的 SelectionKey。上面我們已經討論過 Selector 維護的一種集合 —— SelectionKey 集合,接下來我們再討論另外一種集合 —— SelectedKey 集合。

selectedkey 集合

當 Channel 有就緒 IO 事件之后,對應的 Key 就會被加入到 SelectedKey 集合中,然后這一次 While 循環會依次處理被選擇出來的所有 Key。

但被選擇出來的 Key 可能觸發的是不同的 IO 事件,所以我們需要對 Key 進行區分。代碼里區分了 OP_ACCEPT 和 OP_READ,分別討論一下。

ServerSocketChannel 一開始 register 的時候只設定關注 OP_ACCEPT 事件,所以第一次循環只會進入 IsAcceptable 分支里,所以這里通過 iterator.next() 迭代器拿到的 SelectionKey 就是 serverSocketChannel 注冊之后返回的 Key,同理拿到的 channel 的就是最開始調用 ServerSocketChannel.open(); 創建的 channel。

拿到了 ServerSocketChannel 我們就可以調用其 accept() 方法來處理建立連接的請求了,這里值得注意的是,建立連接之后,這個 SocketChannel 也需要注冊到 Selector 上去,因為這些 SocketChannel 也需要將控制權交給 Selector,這樣后續有就緒 IO 事件才能通過 Selector 處理。這里我們對這個 SocketChannel 只關注 OP_READ 事件。相當于把后續進來的所有的連接和 Selector 就關聯上了。

Accept 事件處理成功之后,服務器這邊會繼續循環,然后再次在 selector.select(); 處阻塞住。

客戶端這邊會繼續調用 write 方法向 channel 寫入數據,數據 Ready 之后就會觸發 OP_READ 事件,然后繼續往下走,這次由于事件是 OP_READ 所以會進入 key.isReadable() 這個分支。進入這個分支之后會獲取到對應的 SocketChannel,并從其中讀取客戶端發來的數據。

而另一個值得關注的是 iterator.remove();,每次迭代都需要把當前處理的 SelectedKey 移除,這是為什么呢?

因為對應的 Key 進入了 SelectedKey 集合之后,不會被 NIO 里的機制給移除。如果我們不去移除,那么下一次調用 selector.selectedKeys().iterator(); 會發現,上次處理的有 OP_ACCEPT 事件的 SelectionKey 還在,而這會導致上面的服務端程序拋出空指針異常。

大家可以自行將 iterator.remove(); 注釋掉再試試

客戶端的代碼很簡單,就直接給出來了:

public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("localhost", 8080));

ByteBuffer buffer = ByteBuffer.allocate(16);
buffer.put("test".getBytes(StandardCharsets.UTF_8));

buffer.flip();
socketChannel.write(buffer);
}

如果不去移除的話,服務端會在下面這行 NPE。

socketChannel.configureBlocking(false);

為啥呢?因為此時 SelectionKey 雖然還在,ServerSocketChannel 也能拿到,但調用 channel.accept(); 的時候,并沒有客戶端真正在發起連接(上一個循環已經處理過真正的連接請求了,只是沒有將這個 Key 從 SelectedKey 中移除)。所以 channel.accept(); 會返回一個 null,我們再對 null 調用 configureBlocking 方法,自然而然就 NPE 了。

責任編輯:姜華 來源: SH的全棧筆記
相關推薦

2011-12-12 10:33:47

JavaNIO

2011-12-12 10:19:00

JavaNIO

2011-12-07 14:41:51

JavaNIO

2021-06-11 17:26:06

代碼Java網絡編程

2021-03-25 09:58:22

鴻蒙HarmonyOS應用開發

2025-02-28 09:14:09

JavaNIO機制

2011-12-08 10:24:53

JavaNIO

2022-01-12 07:36:01

Java數據ByteBuffer

2021-06-07 08:04:39

Restorecon命令安全

2022-01-19 22:14:36

Apache APIAPI 網關插件

2011-12-15 09:55:47

javanio

2011-12-15 11:19:08

JavaNIO

2011-12-07 14:57:44

JavaNIO

2011-12-15 09:40:06

Javanio

2024-05-27 08:04:41

2011-03-11 09:51:47

Java NIO

2020-05-06 22:07:53

UbuntuLinux操作系統

2021-11-29 10:24:56

WasmEnvoy 負載均衡

2024-03-06 11:38:12

Appwrite方式Supabase

2021-02-07 23:58:10

單例模式對象
點贊
收藏

51CTO技術棧公眾號

婷婷视频在线观看| 久久久久久久9999| 成人涩涩视频| 国产精品久久二区二区| 96pao国产成视频永久免费| 国产黄色小视频网站| 国产suv精品一区二区四区视频| 亚洲一区视频在线观看视频| 看高清中日韩色视频| 亚洲一级黄色大片| 狠狠入ady亚洲精品经典电影| 亚洲成人动漫在线播放| 成年人视频网站免费观看| av在线免费观看网| 成人一级视频在线观看| 日本人成精品视频在线| 手机在线免费看毛片| 色狼人综合干| 日韩视频在线你懂得| 人妻丰满熟妇av无码区app| 伊人福利在线| 国产网红主播福利一区二区| aa日韩免费精品视频一| 日韩欧美一级大片| 亚洲精品黄色| 久久激情五月丁香伊人| 大黑人交xxx极品hd| 国产精品亚洲综合在线观看 | 5566中文字幕| 首页亚洲中字| 欧美成人一区二区三区| 五月婷婷六月合| 亚洲人体影院| 亚洲成av人综合在线观看| 在线精品日韩| 成全电影播放在线观看国语| av电影在线观看完整版一区二区| 成人亲热视频网站| 伊人网免费视频| 久久精品30| 97婷婷涩涩精品一区| 欧美三级在线免费观看| 99精品综合| 最近日韩中文字幕中文| 亚洲久久久久久久| 天堂俺去俺来也www久久婷婷 | 一区二区三区日本| 综合久久国产| 欧美边添边摸边做边爱免费| 欧美经典一区二区| 日韩福利在线| 国内三级在线观看| 久久久影院官网| 久久精品二区| 天天干在线观看| a在线欧美一区| 精品在线一区| 亚洲人午夜射精精品日韩| 成人av电影在线播放| 国产精品国产亚洲精品看不卡15| 国产特黄一级片| 韩国视频一区二区| 亚洲综合色激情五月| a天堂中文在线观看| 国产剧情一区二区| 亚洲最大成人网色| 好男人在线视频www| 成人激情综合网站| 精品久久sese| 精品乱码一区二区三四区视频| 久久这里只精品最新地址| 久久久久综合一区二区三区| 男人的天堂在线视频| 久久精品亚洲麻豆av一区二区| 欧美久久久久久久| 999国产在线视频| 一区二区中文字幕在线| 乱子伦一区二区| 乱插在线www| 动漫精品一区二区| 黄色aaa级片| 日韩伦理一区二区| 精品少妇一区二区三区免费观看| 中文字幕无人区二| 一本久久青青| www.亚洲天堂| 香蕉免费毛片视频| 日韩精品成人一区二区在线| 成人欧美一区二区三区在线湿哒哒| 国产男女裸体做爰爽爽| 成人爱爱电影网址| 日韩欧美一区二区视频在线播放 | 欧美重口另类videos人妖| 99精品人妻国产毛片| 男女男精品视频| 9a蜜桃久久久久久免费| 国产在线视频网址| 一区二区在线看| 日韩在线视频在线观看| 欧美a视频在线| 精品国产污污免费网站入口| 亚洲精品国产一区黑色丝袜| 中文av一区| 欧美主播福利视频| 99久久精品国产一区色| 久久综合色天天久久综合图片| 日本一区免费在线观看| 污片在线免费观看| 色综合久久综合网欧美综合网| 天堂av8在线| 日韩黄色网络| 欧美精品在线观看91| 中文字幕手机在线视频| 国产大片一区二区| 五月婷婷综合色| 91禁在线看| 制服视频三区第一页精品| 人妻av无码一区二区三区| 黄色av成人| 国产精品日韩久久久久| 熟妇高潮一区二区三区| 亚洲欧洲制服丝袜| 好男人www社区| 日本中文字幕在线一区| 久久伊人精品一区二区三区| 欧美一区二区三区不卡视频| 国产91丝袜在线18| 色姑娘综合av| 亚洲欧洲高清| 日韩av一卡二卡| 精品97人妻无码中文永久在线| 免费在线视频一区| 日本午夜精品电影| 涩涩视频在线播放| 亚洲第一网站免费视频| 国产精品久久久精品四季影院| 日韩电影一区二区三区| 免费精品视频一区二区三区| 国产美女情趣调教h一区二区| 欧美日韩精品一区二区三区蜜桃| 亚洲做受高潮无遮挡| 一区二区毛片| 极品尤物一区二区三区| 欧美色图天堂| 日韩欧美中文字幕精品| 久久久久久久麻豆| 精品亚洲成a人| 亚洲最大色综合成人av| 男女啪啪999亚洲精品| 在线观看国产精品淫| 波多野结衣一二区| 久久精品一区二区三区av| 免费日韩中文字幕| 国产精品手机在线播放| 日韩av成人在线| 精品无人乱码| 欧美日本韩国一区二区三区视频 | 亚洲日本韩国一区| 亚洲日本黄色片| 亚洲一区二区日韩| 2022国产精品| 日本高清在线观看| 精品国产乱码久久久久久老虎| 国产小视频在线观看免费| 成人免费毛片app| jizzjizz国产精品喷水| 国产精品一区高清| 国产精品爽黄69| 黄色在线免费看| 欧美一区二区黄| 免费一级a毛片夜夜看 | 在线免费观看日韩av| 免费在线亚洲| 五月婷婷综合色| 日本一区影院| 性色av一区二区三区免费| 天堂资源最新在线| 欧美性色综合网| 少妇被躁爽到高潮无码文| 国产精品99久久久久久久vr| 人妻夜夜添夜夜无码av| 中文字幕av一区二区三区人| 国产精品人成电影| av免费在线网站| 日韩电影免费观看在线观看| jizz国产在线| 亚洲永久精品国产| av中文字幕免费观看| 国产专区欧美精品| 亚洲人成无码网站久久99热国产| 精品国产91久久久久久浪潮蜜月| 成人春色激情网| 日韩欧美精品一区二区三区| 日韩在线观看成人| 午夜在线视频免费| 欧美日韩一区二区电影| 精品无码av在线| 国产农村妇女毛片精品久久麻豆 | 久久久精品视频网站| 成人欧美一区二区三区黑人麻豆| 少妇高潮一69aⅹ| 媚黑女一区二区| 99久久99久久精品| 国产成人精品一区二区免费看京| 91视频国产一区| 超级碰碰久久| 欧美黑人性视频| www亚洲人| 精品国产制服丝袜高跟| 成人黄色免费网| 欧美日韩精品二区| 欧美日韩免费一区二区| 中文字幕免费在线观看视频一区| xxxx视频在线观看| 老司机免费视频一区二区三区| 国产av人人夜夜澡人人爽麻豆| 97精品国产| 日本午夜精品电影| 亚欧日韩另类中文欧美| 91视频婷婷| 少妇高潮一区二区三区99| 国产91在线播放九色快色| www.51av欧美视频| 久久91精品国产| 黄色小网站在线观看| 亚洲人成网站777色婷婷| 国精品人妻无码一区二区三区喝尿 | 人人鲁人人莫人人爱精品| 欧美华人在线视频| wwwav在线| 一区二区欧美激情| 日本v片在线免费观看| 精品国产91乱码一区二区三区| 在线观看视频中文字幕| 色播五月激情综合网| 男女视频免费看| 亚洲va欧美va人人爽午夜| 欧美爱爱小视频| 亚洲精品乱码久久久久| 国产3级在线观看| 国产精品国产三级国产普通话99 | 欧美激情国内自拍| 麻豆成人91精品二区三区| 成人性做爰aaa片免费看不忠| 国产日韩欧美一区| 国内自拍在线观看| 亚洲综合国产| 农村妇女精品一二区| 亚洲综合日本| 热久久精品免费视频| 葵司免费一区二区三区四区五区| 欧美二区在线视频| 亚洲尤物在线| 黄色免费网址大全| 久热成人在线视频| 污污的视频免费观看| 国内精品伊人久久久久av一坑| 亚洲精品乱码久久久久久动漫| 精品一区二区久久| 激情文学亚洲色图| 国产成人午夜精品影院观看视频 | japanese色系久久精品| 亚洲aa中文字幕| 91精品日本| 久久大片网站| 精品视频日韩| 91免费视频黄| 亚洲黄色视屏| 日韩精品一区二区三区不卡| 秋霞av亚洲一区二区三| 最新免费av网址| 国产99精品在线观看| 免费a在线观看播放| 久久久无码精品亚洲日韩按摩| x88av在线| 亚洲另类一区二区| 国产女同在线观看| 欧美最新大片在线看| 国产精品无码久久久久成人app| 日韩一区二区影院| 午夜国产在线观看| 自拍亚洲一区欧美另类| 色婷婷av在线| 国产99久久精品一区二区 夜夜躁日日躁 | 亚洲色图一区二区| 国产精品第九页| 在线观看国产精品网站| 国产高清在线免费| 亚洲免费视频一区二区| 米奇777四色精品人人爽| 97精品免费视频| 欧美大陆国产| 精品一区久久久| 亚洲激情久久| 99精品免费在线观看| 国产乱人伦精品一区二区在线观看| 国产xxxx视频| 中文字幕一区二区三区乱码在线| 国产亚洲精品久久777777| 在线免费不卡电影| 国产91绿帽单男绿奴| 中文字幕视频在线免费欧美日韩综合在线看 | 日本免费一区二区三区视频| 久久青青草综合| 亚洲破处大片| 成人午夜激情av| 99国产精品视频免费观看| 国产高潮国产高潮久久久91| 日本大香伊一区二区三区| 亚洲黄色在线观看视频| 中文字幕不卡在线视频极品| mm视频在线视频| 91在线观看免费| 欧美熟乱15p| 日日橹狠狠爱欧美超碰| 国产精品99久久久久久久vr| 国产福利在线导航| 狠狠躁夜夜躁人人爽超碰91| 97在线播放免费观看| 亚洲美女av在线播放| xxxx另类黑人| 91福利视频导航| 国产国产精品| 天堂在线资源视频| 久久久精品影视| 国产手机在线视频| 精品日本一线二线三线不卡| 免费高清在线观看| 国产精品入口免费视| 国产99久久精品一区二区300| 国产精品videossex国产高清| 开心九九激情九九欧美日韩精美视频电影| 9.1成人看片| 天天色天天操综合| 蜜臀av免费在线观看| 久久不射热爱视频精品| 人人玩人人添人人澡欧美| 日韩欧美视频一区二区| 日韩主播视频在线| 无码人妻精品一区二区中文| 狠狠久久五月精品中文字幕| 欧美一级淫片aaaaaa| 欧美精品电影在线| 白白在线精品| 欧美午夜性视频| av不卡一区二区三区| 国产精品第56页| 亚洲精品99久久久久中文字幕| ririsao久久精品一区| 国产精品久久亚洲7777| 亚洲国产午夜| 朝桐光av一区二区三区| 午夜av区久久| 色网站在线免费观看| 日本亚洲欧洲色| 欧美一区2区| 欧美美女一级片| 亚洲视频免费看| 亚洲av无码一区二区乱子伦| 欧美激情综合亚洲一二区| 女仆av观看一区| 毛片av免费在线观看| 国产三级欧美三级日产三级99 | 欧美日韩一区在线视频| 久久精品首页| 超碰人人干人人| 制服丝袜成人动漫| 2021天堂中文幕一二区在线观| 国产精品免费视频一区二区 | 国产精品美女一区二区在线观看| 在线免费观看日韩视频| 久久精品国产精品| 超碰地址久久| 国模杨依粉嫩蝴蝶150p| 国产精品高潮呻吟| 成人免费视频国产| 日本精品久久久久久久| 色狮一区二区三区四区视频| 少妇性l交大片7724com| 五月天欧美精品| 福利视频在线导航| 91色视频在线导航| 国产日韩欧美一区| 99成人在线观看| 亚洲成人999| 91精品国产经典在线观看| 六月婷婷激情网| 91网站视频在线观看| 一区二区三区免费在线| 欧美精品videosex性欧美| 激情综合网五月| 被黑人猛躁10次高潮视频| 欧美性xxxx极品hd欧美风情| 美女黄视频在线观看| 久久青青草综合| 国产盗摄视频一区二区三区| 黄色av一区二区| 国产+人+亚洲| 91麻豆精品国产91久久久平台| 日本少妇xxxx|