NIO中的選擇器(Selector)的做用就是維護註冊到選擇器中的通道集合,每個通道與選擇器的關係封裝在選擇鍵(SelectionKey)中,實際上能夠認爲選擇器維護的是選擇鍵集合。建立Selector對象使用Selector.open()。java
Selector類主要維護三個集合:安全
1. Registered key set(已註冊鍵集合):調用Selector的keys()方法能夠獲取服務器
2. Selected key set(已選擇鍵集合):調用Selector的selectedKeys()方法獲取多線程
3. Cancelled key set(已取消鍵集合):已取消鍵集合是Selector對象的私有成員,外部沒法訪問併發
只有繼承了SelectableChannel的類才能註冊到選擇器中,而且只有非阻塞模式的通道才能註冊到選擇器,以下代碼:socket
//建立一個套接字服務器,並註冊到選擇器 //建立選擇器 Selector selector = Selector.open(); //建立Socket服務器通道 ServerSocketChannel ssc = ServerSocketChannel.open(); //綁定65535端口 ssc.socket().bind(new InetSocketAddress(65535)); //設置通道爲非阻塞模式 ssc.configureBlocking(false); //將通道註冊到選擇器,指定通道興趣是等待接收鏈接 SelectionKey key = ssc.register(selector, SelectionKey.OP_ACCEPT);
實際使用中,通常使用while循環輪詢獲取註冊到選擇器中通道選擇器的操做,以下代碼:ide
while (true) { int n = selector.select(); if (n > 0) { Iterator<SelectionKey> iter = selector.selectedKeys().iterator(); while (iter.hasNext()) { SelectionKey keyy = iter.next(); iter.remove(); // ...... } } }
select()方法的做用是選擇註冊到選擇器中通道感興趣的鍵,此方法是阻塞的,直到有感興趣的事件發生;還可使用select(10000)方法設置選擇鍵的超時時間,單位是Millisecond;還可使用selectNow(),此方法是非阻塞,若沒有通道就緒會當即返回0。線程
不須要使用Selector時,調用close()方法能夠關閉選擇器,關閉選擇器後,全部註冊到其中的選擇鍵會被設置爲無效狀態。設計
關閉選擇器後,試圖調用它的方法會拋出ClosedSelectorException,這是一個非運行時異常,全部在使用時有必要檢查選擇器是否打開,使用isOpen()方法。rest
NIO中定義了4中可選擇操做:OP_READ(讀)、OP_WRITE(寫)、OP_CONNECT(鏈接)、OP_ACCEPT(接受),這些常量在SelectionKey中定義。可使用通道的validOps()獲取某個通道上支持的操做集合。
同一個通道能夠被屢次註冊到同一個選擇器中,沒有異常錯誤,也沒有特別的效果,我的以爲這個地方設計的不嚴謹;實際使用中應避免同一個通道屢次註冊到一個選擇器中,雖然對實際功能沒有影響,可是爲了總體思路清晰最好不要這麼作。
註冊一個通道時能夠在這個選擇鍵上設置一個Object對象,好比在接受鏈接操做中設置,在讀操做中獲取,以下代碼:
SelectionKey keyy = iter.next(); iter.remove(); if(keyy.isAcceptable()){ ServerSocketChannel channel = (ServerSocketChannel) keyy.channel(); SocketChannel sc = channel.accept(); sc.register(selector, SelectionKey.OP_READ, "hello"); } if(keyy.isReadable()){ //attach的值爲"hello" Object attach = keyy.attachment(); SocketChannel sc = (SocketChannel) keyy.channel(); }
SelectionKey(選擇鍵對象)提供了下面方法:
channel():獲取關聯的通道(SelectableChannel) selector():獲取關聯的選擇器(Selector) isValid():驗證維護選擇器與通道關係的SelectionKey是否有效 cancel():取消鍵,調用一個已取消的鍵的方法將拋出CancelledKeyException interestOps():獲取這個key的興趣(可選擇操做)集合 interestOps(int ops):設置這個key的興趣 readyOps():返回這個key準備好的操做集合(興趣集合) isReadable():檢查選擇鍵的興趣是否爲可讀,其實是通道的興趣,選擇鍵維護通道與選擇器關係 isWritable():檢查選擇鍵的興趣是否爲可寫 isConnectable():檢查選擇鍵的興趣是否爲鏈接 isAcceptable():檢查選擇鍵的興趣是否爲接受 attach(Object ob):設置一個Object數據到此key上 attachment():獲取設置的Object數據
能夠設置null來清楚SelectionKey的附件對象,若是SelectionKey的存在週期很長,可是附件對象不須要存在很長,必定要再使用完後即時清理附件對象,不然附件對象不能被GC回收,可能會發生內存泄露。
中止選擇過程可使用Selector的wakeup()方法,此方法能夠安全的退出select()阻塞,
選擇器對象(Selector)是線程安全的,在多線程併發訪問不會有問題。
Selector(選擇器)提供了下面方法:
open():打開一個選擇器 isOpen():檢查一個選擇器實例是否打開 provider():返回一個SelectorProvider keys():返回註冊鍵集合 selectedKeys():返回已選擇鍵集合 selectNow():馬上執行選擇,非阻塞,若沒有已準備好的通道則當即返回0 select(long timeout):執行選擇,超過指定毫秒數則返回 select():執行選擇,會一直阻塞直到有準備就緒的通道 wakeup():中止選擇 close():關閉選擇器
使用Selector類可能會拋出的錯誤:
IllegalBlockingModeException:註冊一個阻塞狀態的通道拋出此異常 ClosedChannelException:註冊一個已關閉的通道拋出此異常 ClosedSelectorException:調用一個已關閉的選擇器的方法拋出此異常 UnsupportedOperationException:已註冊的鍵集合是隻讀,修改它會拋出此異常