精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

精选一区二区三区四区五区| 在线观看精品国产视频| 成人免费视频91| 天天摸夜夜添狠狠添婷婷| 亚洲黄色精品| 精品调教chinesegay| 精品视频无码一区二区三区| 成人激情电影在线看| 天使萌一区二区三区免费观看| 亚洲欧洲日本专区| 亚洲美女性囗交| 中文字幕伦理免费在线视频| 国产乱色国产精品免费视频| 久久久久久国产精品久久| www.88av| 成人福利片在线| 国产精品私人影院| www.成人av.com| 日本a级c片免费看三区| 亚洲成人99| 日韩电视剧免费观看网站| 高清一区二区视频| 日韩伦理av| 久久久久9999亚洲精品| 成人黄色中文字幕| 在线观看免费av片| 亚洲综合中文| 亚洲欧美国产一本综合首页| 三级黄色片免费看| 精品国产免费人成网站| 亚洲素人一区二区| 欧美视频小说| 刘亦菲毛片一区二区三区| 日韩精品一区第一页| 欧美国产一区二区三区| 国产性猛交xx乱| 狼人精品一区二区三区在线| 欧美日韩在线免费视频| 伊人久久在线观看| 91在线不卡| 国产丶欧美丶日本不卡视频| 国产精欧美一区二区三区| 久久久久97国产| 日韩一区电影| 精品伊人久久97| 国产1区2区在线| 国产不卡在线| 国产欧美日韩激情| 欧美下载看逼逼| 高清乱码毛片入口| 国产乱码精品一区二区三区av | 92国产精品视频| 成年人av网站| 日韩视频中文| 欧美国产乱视频| 可以免费看av的网址| 狠狠色丁香婷婷综合影院| 日韩电影中文字幕| 精品少妇人妻av一区二区三区| 国产午夜亚洲精品一级在线| 欧美日韩高清一区二区不卡 | 自拍偷拍第9页| 嫩草一区二区三区| 亚洲精品福利在线观看| 又黄又色的网站| 99香蕉久久| 精品电影一区二区三区| 久久久久国产免费| 超碰在线一区| 337p日本欧洲亚洲大胆色噜噜| 精品人妻人人做人人爽夜夜爽| 91精品国产色综合久久不卡粉嫩| 欧美日韩精品一二三区| 午夜免费福利视频在线观看| 日本中文字幕视频一区| 4438x亚洲最大成人网| 看看黄色一级片| 电影91久久久| 欧美一区二区三区影视| 精品人妻无码中文字幕18禁| 免费观看性欧美大片无片| 日韩亚洲欧美综合| 免费观看一区二区三区| 精品亚洲自拍| 亚洲欧洲一区二区三区久久| 欧美激情 一区| 色中色综合网| 久久影院资源网| 国产一级在线观看视频| 国产精品久久国产愉拍| 日韩暖暖在线视频| 亚洲无码精品国产| 国产v综合v亚洲欧| 免费观看国产成人| av福利在线播放| 亚洲免费色视频| 免费看日本毛片| 精品成人av| 欧美丰满少妇xxxxx高潮对白 | 美女视频一区| 91精品国产综合久久小美女| 中文字幕第九页| 国产乱人伦精品一区| 亚洲精品一区二区网址| 亚洲天堂网av在线| 99亚洲一区二区| 国产精品久久久久影院日本| 精品毛片一区二区三区| 91蜜桃传媒精品久久久一区二区| 亚洲精品中文字幕乱码三区不卡| 日本在线观看高清完整版| 精品欧美aⅴ在线网站| 在线免费av播放| 中文字幕av一区二区三区四区| 国产婷婷97碰碰久久人人蜜臀| 亚洲天堂精品一区| 亚洲欧洲一区| 国产自摸综合网| 亚洲色图狠狠干| 亚洲人成电影网站色mp4| 1024精品视频| 狂野欧美xxxx韩国少妇| 亚洲人成网站在线播| 中文字幕另类日韩欧美亚洲嫩草| 欧美亚洲一区| 3d动漫啪啪精品一区二区免费| 免费理论片在线观看播放老| 亚洲免费av观看| 免费观看成人网| 亚洲精品福利| 久久精品成人欧美大片| 800av免费在线观看| 国产乱一区二区| 亚洲精品第一区二区三区| 华人av在线| 欧美va亚洲va在线观看蝴蝶网| 偷拍女澡堂一区二区三区| 欧美激情国产在线| 日本一区二区不卡| 日批免费在线观看| 亚洲视频免费看| 中文字幕永久有效| 精品影片在线观看的网站| 欧美精品久久久久久久久| 6—12呦国产精品| 国产亚洲综合性久久久影院| 黄页免费在线观看视频| 1204国产成人精品视频| 久久成人这里只有精品| 在线中文字幕网站| 久久精品综合网| 国内性生活视频| 豆花视频一区二区| 欧美丰满少妇xxxxx| 国产老女人乱淫免费| 国产精品丝袜一区| www.这里只有精品| 清纯唯美综合亚洲| 国产精品视频地址| 精品三级久久久久久久电影聊斋| 天天av天天翘天天综合网| 精品国产乱码久久久久夜深人妻| 欧美在线不卡| 91香蕉亚洲精品| 国产精品剧情| 精品日产卡一卡二卡麻豆| 精品国产视频一区二区三区| 九九久久精品视频| 中文字幕中文字幕在线中心一区 | 色婷婷亚洲一区二区三区| 先锋资源av在线| 中文亚洲欧美| 欧美精品一区二区视频| 国模一区二区| 久久国产一区二区三区| 国产免费黄色录像| 一区二区三区资源| 国产麻豆剧传媒精品国产av| 99亚洲视频| 日本一区二区三区视频在线播放 | 一区二区三区免费在线观看| 91网址在线观看精品| 亚洲午夜精品一区 二区 三区| 亚洲一区二区中文| 免费影视亚洲| 亚洲激情视频在线播放| 丰满少妇xoxoxo视频| 欧美国产日产图区| www.51色.com| 一本久道久久综合婷婷鲸鱼| 免费精品视频一区二区三区| 日韩经典一区| 久久视频在线看| 日本xxxx人| 亚洲成人动漫在线观看| 久久无码人妻精品一区二区三区| 日韩黄色免费网站| 亚洲第一综合| 综合伊人久久| 国产精品v日韩精品| 成年人在线观看| 日韩欧美视频在线| 天天干,天天干| 亚洲欧美一区二区在线观看| 色综合久久五月| 日韩福利电影在线观看| 黄色一级片黄色| 欧美色图在线播放| 国产精品久久久久久久久久直播| 电影天堂国产精品| 欧美高清无遮挡| 国产在线观看黄| 欧美变态tickling挠脚心| 中文字幕高清在线免费播放| 最新高清无码专区| 91精品人妻一区二区三区蜜桃欧美| 久久99精品久久只有精品| 久久久亚洲国产精品| 91亚洲国产| 欧美精品在线一区| a级日韩大片| 国产欧美一区二区三区在线| 激情视频网站在线播放色| www高清在线视频日韩欧美| 99久久国产免费| 在线视频一区二区三区| 久久久精品国产sm调教| 亚洲国产高清在线| 中文字幕一区三区久久女搜查官| 精品夜夜嗨av一区二区三区| 不要播放器的av网站| 99久久99久久精品国产片桃花| 草莓视频一区| 日本免费成人| 国产精品欧美亚洲777777| 欧美激情网站| 久久久久久久久国产| www免费在线观看| 中文字幕日韩欧美在线| 巨骚激情综合| 亚洲精品丝袜日韩| 免费av一级片| 欧美成人video| 国产又粗又黄又爽的视频| 色综合久久六月婷婷中文字幕| 国产奶水涨喷在线播放| 亚洲精品国产一区二区三区四区在线| 精品女人久久久| 中文天堂在线一区| 天堂久久精品忘忧草| 久久亚洲一级片| 屁屁影院国产第一页| 风间由美性色一区二区三区| 五月天六月丁香| 国产精品综合网| 8x8x成人免费视频| 久久国产剧场电影| 三级视频中文字幕| 久久国产精品无码网站| 中文字幕第38页| 久久99久久久久久久久久久| 欧美伦理片在线观看| 蜜桃免费网站一区二区三区| 在线视频日韩一区 | 伊人网在线综合| 久久成人免费日本黄色| 超碰人人草人人| 国产美女一区二区| 一级淫片在线观看| 国产一区二区在线观看免费| 99久久综合网| 激情六月婷婷久久| 超碰人人cao| 成人av午夜电影| 色婷婷av777| 国产精品网站导航| 三级黄色在线观看| 亚洲黄色小视频| 日本一级黄色大片| 欧美日韩一区二区三区| 天堂中文字幕在线观看| 日本高清无吗v一区| 最近中文字幕在线视频| 欧美乱妇一区二区三区不卡视频| 国产欧美日韩成人| 亚洲电影在线看| 男生女生差差差的视频在线观看| 一本色道久久综合亚洲精品小说| 日韩专区在线| 久久久亚洲精品视频| www.com.cn成人| 国产精品视频久久久久| 日韩一二三区| 欧美成人蜜桃| 一区二区中文| 免费看一级大黄情大片| 青青草精品视频| 国产免费无码一区二区| 99久久综合色| wwwwww日本| 国产精品超碰97尤物18| 久久精品波多野结衣| 午夜精品在线视频一区| 一级特黄免费视频| 欧美xxxxxxxx| 3d成人动漫在线| 欧美—级a级欧美特级ar全黄| 僵尸再翻生在线观看| 国产免费成人av| 青青一区二区| 亚洲黄色一区二区三区| 国产精品videossex久久发布| 国产成人a亚洲精v品无码| 日日夜夜精品视频免费| 爱情岛论坛亚洲自拍| 国产日韩高清在线| 国产亚洲精品女人久久久久久| 在线观看日韩毛片| 亚洲欧美高清视频| 这里只有精品丝袜| 美女网站在线看| 91亚洲国产精品| 国产一区二区三区网| 久久av综合网| 久久爱www久久做| 97伦伦午夜电影理伦片| 亚洲专区一二三| 在线观看免费视频a| 亚洲欧美日韩国产精品| sis001亚洲原创区| 亚洲在线视频福利| 久久免费大视频| av片中文字幕| 成人av在线电影| 久久午夜无码鲁丝片午夜精品| 欧美日韩国产bt| 91xxx在线观看| 日本精品视频网站| 日本中文字幕在线一区| www.18av.com| 国产一区在线不卡| 2017亚洲天堂| 欧美乱妇15p| 拍真实国产伦偷精品| 国产精品久久久久av| 色综合www| 人人干视频在线| 成人一区二区三区中文字幕| 免费网站看av| 日韩视频国产视频| 黄污视频在线观看| 国产精品亚洲不卡a| 国内视频精品| 制服丝袜在线第一页| 亚洲电影一级黄| 人妻丰满熟妇av无码区hd| 久久久久久久久亚洲| 国产66精品| 色综合久久久久无码专区| 北条麻妃一区二区三区| 日本一区二区三区四区五区| 精品sm在线观看| 成年人视频免费在线播放| 国产精品二区在线观看| 亚洲国产精品第一区二区| 色婷婷精品久久二区二区密| 亚洲成人精品影院| 日韩三级电影网| 日韩暖暖在线视频| 日韩在线视屏| 国产一区二区在线观看免费视频| 中文字幕佐山爱一区二区免费| 国产高清精品软件丝瓜软件| 欧美高清不卡在线| 网曝91综合精品门事件在线| 国产日产欧美视频| 欧美国产日产图区| 国产美女精品视频国产| 欧美激情精品久久久久久久变态 | 亚洲一区二区三区乱码aⅴ蜜桃女| 欧美电影免费观看高清| www.色就是色.com| 一区二区三区在线观看欧美| 黄色aaa毛片| 欧美一级bbbbb性bbbb喷潮片| 六月丁香久久丫| 国产精品少妇在线视频| 中文字幕一区二区三| 人人妻人人澡人人爽久久av| 日韩美女视频免费在线观看| 亚洲a一区二区三区| 美女扒开腿免费视频| 日韩欧美国产中文字幕| 日韩av中文| 国产一区二区视频在线免费观看| 免费永久网站黄欧美| 萌白酱视频在线| 亚洲国产精品嫩草影院久久| 91亚洲精品| 久久99久久99精品|