亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Java NIO(六)Selector

系統(tǒng) 3283 0
Selector(選擇器)是Java NIO中能夠檢測(cè)一到多個(gè)NIO通道,并能夠知曉通道是否為諸如讀寫事件做好準(zhǔn)備的組件。這樣,一個(gè)單獨(dú)的線程可以管理多個(gè)channel,從而管理多個(gè)網(wǎng)絡(luò)連接。

為什么使用Selector?
僅用單個(gè)線程來處理多個(gè)Channels的好處是,只需要更少的線程來處理通道。事實(shí)上,可以只用一個(gè)線程處理所有的通道。對(duì)于操作系統(tǒng)來說,線程之間上下文切換的開銷很大,而且每個(gè)線程都要占用系統(tǒng)的一些資源(如內(nèi)存)。因此,使用的線程越少越好。

但是,需要記住,現(xiàn)代的操作系統(tǒng)和CPU在多任務(wù)方面表現(xiàn)的越來越好,所以多線程的開銷隨著時(shí)間的推移,變得越來越小了。實(shí)際上,如果一個(gè)CPU有多個(gè)內(nèi)核,不使用多任務(wù)可能是在浪費(fèi)CPU能力。不管怎么說,關(guān)于那種設(shè)計(jì)的討論應(yīng)該放在另一篇不同的文章中。在這里,只要知道使用Selector能夠處理多個(gè)通道就足夠了。
下面是單線程使用一個(gè)Selector處理3個(gè)channel的示例圖:
Java NIO(六)Selector

Selector的創(chuàng)建
通過調(diào)用Selector.open()方法創(chuàng)建一個(gè)Selector,如下:
    Selector selector = Selector.open();
  


向Selector注冊(cè)通道
為了將Channel和Selector配合使用,必須將channel注冊(cè)到selector上。通過SelectableChannel.register()方法來實(shí)現(xiàn),如下:
    
channel.configureBlocking(false);
SelectionKey key = channel.register(selector,
Selectionkey.OP_READ);

  

與Selector一起使用時(shí),Channel必須處于非阻塞模式下。這意味著不能將FileChannel與Selector一起使用,因?yàn)镕ileChannel不能切換到非阻塞模式。而套接字通道都可以。

注意register()方法的第二個(gè)參數(shù)。這是一個(gè)“interest集合”,意思是在通過Selector監(jiān)聽Channel時(shí)對(duì)什么事件感興趣。可以監(jiān)聽四種不同類型的事件:
  • Connect
  • Accept
  • Read
  • Write

通道觸發(fā)了一個(gè)事件意思是該事件已經(jīng)就緒。所以,某個(gè)channel成功連接到另一個(gè)服務(wù)器稱為“連接就緒”。一個(gè)server socket channel準(zhǔn)備好接收新進(jìn)入的連接稱為“接收就緒”。一個(gè)有數(shù)據(jù)可讀的通道可以說是“讀就緒”。等待寫數(shù)據(jù)的通道可以說是“寫就緒”。

這四種事件用SelectionKey的四個(gè)常量來表示:
  • SelectionKey.OP_CONNECT
  • SelectionKey.OP_ACCEPT
  • SelectionKey.OP_READ
  • SelectionKey.OP_WRITE

如果你對(duì)不止一種事件感興趣,那么可以用“位或”操作符將常量連接起來,如下:
    int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
  

在下面還會(huì)繼續(xù)提到interest集合。

SelectionKey
在上一小節(jié)中,當(dāng)向Selector注冊(cè)Channel時(shí),register()方法會(huì)返回一個(gè)SelectionKey對(duì)象。這個(gè)對(duì)象包含了一些你感興趣的屬性:
interest集合
ready集合
Channel
Selector
附加的對(duì)象(可選)

interest集合
就像向Selector注冊(cè)通道一節(jié)中所描述的,interest集合是你所選擇的感興趣的事件集合。可以通過SelectionKey讀寫interest集合,像這樣:
    
int interestSet = selectionKey.interestOps();
boolean isInterestedInAccept  = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = (interestSet & SelectionKey.OP_CONNECT) == SelectionKey.OP_CONNECT;
boolean isInterestedInRead    = (interestSet & SelectionKey.OP_READ) == SelectionKey.OP_READ;
boolean isInterestedInWrite   = (interestSet & SelectionKey.OP_WRITE) == boolean isInterestedInWrite;

  

可以看到,用“位與”操作interest 集合和給定的SelectionKey常量,可以確定某個(gè)確定的事件是否在interest 集合中。

ready集合
ready 集合是通道已經(jīng)準(zhǔn)備就緒的操作的集合。在一次選擇(Selection)之后,你會(huì)首先訪問這個(gè)ready set。Selection將在下一小節(jié)進(jìn)行解釋。可以這樣訪問ready集合:
    int readySet = selectionKey.readyOps();
  

可以用像檢測(cè)interest集合那樣的方法,來檢測(cè)channel中什么事件或操作已經(jīng)就緒。但是,也可以使用以下四個(gè)方法,它們都會(huì)返回一個(gè)布爾類型:
    
selectionKey.isAcceptable();
selectionKey.isConnectable();
selectionKey.isReadable();
selectionKey.isWritable();

  


Channel + Selector
從SelectionKey訪問Channel和Selector很簡(jiǎn)單。如下:
    
Channel  channel  = selectionKey.channel();
Selector selector = selectionKey.selector();

  


附加的對(duì)象
可以將一個(gè)對(duì)象或者更多信息附著到SelectionKey上,這樣就能方便的識(shí)別某個(gè)給定的通道。例如,可以附加 與通道一起使用的Buffer,或是包含聚集數(shù)據(jù)的某個(gè)對(duì)象。使用方法如下:
    
selectionKey.attach(theObject);
Object attachedObj = selectionKey.attachment();

  

還可以在用register()方法向Selector注冊(cè)Channel的時(shí)候附加對(duì)象。如:
    
SelectionKey key = channel.register(selector, SelectionKey.OP_READ,theObject);

  


通過Selector選擇通道
一旦向Selector注冊(cè)了一或多個(gè)通道,就可以調(diào)用幾個(gè)重載的select()方法。這些方法返回你所感興趣的事件(如連接、接受、讀或?qū)懀┮呀?jīng)準(zhǔn)備就緒的那些通道。換句話說,如果你對(duì)“讀就緒”的通道感興趣,select()方法會(huì)返回讀事件已經(jīng)就緒的那些通道。

下面是select()方法:
int select()
int select(long timeout)
int selectNow()

select()阻塞到至少有一個(gè)通道在你注冊(cè)的事件上就緒了。
select(long timeout)和select()一樣,除了最長(zhǎng)會(huì)阻塞timeout毫秒(參數(shù))。
selectNow()不會(huì)阻塞,不管什么通道就緒都立刻返回(譯者注:此方法執(zhí)行非阻塞的選擇操作。如果自從前一次選擇操作后,沒有通道變成可選擇的,則此方法直接返回零。)。

select()方法返回的int值表示有多少通道已經(jīng)就緒。亦即,自上次調(diào)用select()方法后有多少通道變成就緒狀態(tài)。如果調(diào)用select()方法,因?yàn)橛幸粋€(gè)通道變成就緒狀態(tài),返回了1,若再次調(diào)用select()方法,如果另一個(gè)通道就緒了,它會(huì)再次返回1。如果對(duì)第一個(gè)就緒的channel沒有做任何操作,現(xiàn)在就有兩個(gè)就緒的通道,但在每次select()方法調(diào)用之間,只有一個(gè)通道就緒了。

selectedKeys()
一旦調(diào)用了select()方法,并且返回值表明有一個(gè)或更多個(gè)通道就緒了,然后可以通過調(diào)用selector的selectedKeys()方法,訪問“已選擇鍵集(selected key set)”中的就緒通道。如下所示:
    Set selectedKeys = selector.selectedKeys();
  

當(dāng)像Selector注冊(cè)Channel時(shí),Channel.register()方法會(huì)返回一個(gè)SelectionKey 對(duì)象。這個(gè)對(duì)象代表了注冊(cè)到該Selector的通道。可以通過SelectionKey的selectedKeySet()方法訪問這些對(duì)象。

可以遍歷這個(gè)已選擇的鍵集合來訪問就緒的通道。如下:
    
Set selectedKeys = selector.selectedKeys();
Iterator 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.<tuihighlight class="tuihighlight">remove</tuihighlight>();
}

  

這個(gè)循環(huán)遍歷已選擇鍵集中的每個(gè)鍵,并檢測(cè)各個(gè)鍵所對(duì)應(yīng)的通道的就緒事件。

注意每次迭代末尾的keyIterator.remove()調(diào)用。Selector不會(huì)自己從已選擇鍵集中移除SelectionKey實(shí)例。必須在處理完通道時(shí)自己移除。下次該通道變成就緒時(shí),Selector會(huì)再次將其放入已選擇鍵集中。

SelectionKey.channel()方法返回的通道需要轉(zhuǎn)型成你要處理的類型,如ServerSocketChannel或SocketChannel等。

wakeUp()
某個(gè)線程調(diào)用select()方法后阻塞了,即使沒有通道已經(jīng)就緒,也有辦法讓其從select()方法返回。只要讓其它線程在第一個(gè)線程調(diào)用select()方法的那個(gè)對(duì)象上調(diào)用Selector.wakeup()方法即可。阻塞在select()方法上的線程會(huì)立馬返回。

如果有其它線程調(diào)用了wakeup()方法,但當(dāng)前沒有線程阻塞在select()方法上,下個(gè)調(diào)用select()方法的線程會(huì)立即“醒來(wake up)”。

close()
用完Selector后調(diào)用其close()方法會(huì)關(guān)閉該Selector,且使注冊(cè)到該Selector上的所有SelectionKey實(shí)例無效。通道本身并不會(huì)關(guān)閉。

完整的示例
這里有一個(gè)完整的示例,打開一個(gè)Selector,注冊(cè)一個(gè)通道注冊(cè)到這個(gè)Selector上(通道的初始化過程略去),然后持續(xù)監(jiān)控這個(gè)Selector的四種事件(接受,連接,讀,寫)是否就緒。
    
Selector selector = Selector.open();
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
while(true) {
  int readyChannels = selector.select();
  if(readyChannels == 0) continue;
  Set selectedKeys = selector.selectedKeys();
  Iterator 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();
  }
}

  

Java NIO(六)Selector


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 午夜影院操一 | 免费观看a级完整视频 | 久草精品在线观看 | 欧美日韩综合高清一区二区 | 欧美性影院 | 久久影视网 | 日本猛妇色xxxxx在线 | 日本一区二区三区免费在线观看 | 久久中文字幕一区二区三区 | 中文字幕一区二区三区 精品 | 奇米色888 | 久久网精品视频 | 久久视频在线观看免费 | 99精品视频看国产啪视频 | 9热这里只有真品 | 中文字幕 一区 婷婷 在线 | 99久久99久久久精品久久 | 欧美人在线一区二区三区 | 国产精品日韩一区二区三区 | 国产老熟| 中文字幕在线视频观看 | 欧美成人性生活视频 | porno日本xxxxx视频 | 韩日性视频 | 视频一区精品 | 亚洲精品久久久久中文字小说 | 亚洲综合精品一区 | 日日插天天干 | 久久99精品一久久久久久 | 天天射天天添 | 香港a毛片免费全部播放 | 99综合之综合久久伊人 | 不卡视频免费在线观看 | 天天舔天天射天天操 | 欧美一区二区三区免费视频 | 久久一本精品久久精品66 | 亚洲成人第一页 | 国产精品大片天天看片 | 五月天国产精品 | 日韩一区二区精品久久高清 | 日本激情视频一区二区三区 |