色婷婷综合在线,在线日韩欧美一区二区三区,欧美日韩中文字幕在线,一区国产视频,极品嫩模,h美女漫画网站,亚洲wuma

您的位置:首頁>熱點推薦 >

Java 網(wǎng)絡(luò)編程 —— 非阻塞式編程

2023-05-14 16:25:03    來源:博客園
線程阻塞概述

在生活中,最常見的阻塞現(xiàn)象是公路上汽車的堵塞。汽車在公路上快速行駛,如果前方交通受阻,就只好停下來等待,等到公路順暢,才能恢復(fù)行駛。


【資料圖】

線程在運行中也會因為某些原因而阻塞。所有處于阻塞狀態(tài)的線程的共同特征:放棄 CPU,暫停運行,只有等到導(dǎo)致阻塞的原因消除,才能恢復(fù)運行,或者被其他線程中斷該線程會退出阻塞狀態(tài),并且拋出 InterruptedException

導(dǎo)致線程阻塞的原因主要有以下方面:

線程執(zhí)行了 Threadsleep(int n)方法,線程放棄 CPU,睡眠 n ms,然后恢復(fù)運行線程要執(zhí)行一段同步代碼,由于無法獲得相關(guān)的同步鎖,只好進入阻塞狀態(tài),等到獲取同步鎖再恢復(fù)運行線程執(zhí)行了一個對象的 wait()方法,進入阻塞狀態(tài),只有等到其他線程執(zhí)行了該對象的 notify()notifyAll()方法,才可能將其喚醒線程執(zhí)行 IO 操作或進行遠程通信時,會因為等待相關(guān)的資源而進入阻塞狀態(tài)

進行遠程通信時,在客戶程序中,線程在以下情況下可能進入阻塞狀態(tài):

請求與服務(wù)器建立連接時,即當線程執(zhí)行 Socket 的帶參數(shù)的構(gòu)造方法,或執(zhí)行 Socke 的 connect()方法時,會進入阻塞狀態(tài),直到連接成功,此線程才從 Socket 的構(gòu)造方法或 connect()方法返回

線程從 Socket 的輸入流讀入數(shù)據(jù)時,如果沒有足夠的數(shù)據(jù),就會進入阻塞狀態(tài),直到讀到了足夠的數(shù)據(jù),或者到達輸入流的末尾,或者出現(xiàn)了異常,才從輸入流的 read()方法返回或異常中斷

輸入流中有多少數(shù)據(jù)才算足夠呢?這要看線程執(zhí)行的 read()方法的類:

int read():只要輸入流中有 1 字節(jié),就算足夠int read(byte[] buf):只要輸入流中的字節(jié)數(shù)目與參數(shù) buff 數(shù)組的長度相同,就算足夠String readLine():只要輸入流中有 1 行字符,就算足夠

線程向 Socket 的輸出流寫一批數(shù)據(jù)時,可能會進入阻塞狀態(tài),等到輸出了所有的數(shù)據(jù),或者出現(xiàn)異常,才從輸出流的 write()方法返回或異常中斷

如果調(diào)用 Socket 的 setSoLinger()方法設(shè)置了關(guān)閉 Socket 的延遲時間,那么當線程執(zhí)行 Socket 的 close()方法時,會進入阻塞狀態(tài),直到底層 Socket 發(fā)送完所有剩余數(shù)據(jù)或者超過了 setSoLinger()方法設(shè)置的延遲時間,才從 close()方法返回

在服務(wù)器程序中,線程在以下情況下可能會進入阻塞狀態(tài):

線程執(zhí)行 ServerSocket 的 accept()方法,等待客戶的連接,直到接收到了客戶連接才從 accept()方法返回線程從 Socket 的輸入流讀入數(shù)據(jù)時,如果輸入流沒有足夠的數(shù)據(jù)就會進入阻塞狀態(tài)線程向 Socket 的輸出流寫一批數(shù)據(jù)時,可能會進入阻塞狀態(tài),等到輸出了所有的數(shù)據(jù),或者出現(xiàn)異常,才從輸出流的 write()方法返回或異常中斷

由此可見,無論是在服務(wù)器程序還是客戶程序中,當通過 Socket 的輸入流和輸出流讀寫數(shù)據(jù)時,都可能進入阻塞狀態(tài)。這種可能出現(xiàn)阻塞的輸入和輸出操作被稱為阻塞 IO。與此對照,如果執(zhí)行輸入和輸出操作時,不會發(fā)生阻塞,則稱為非阻塞 IO

非阻塞通信的基本思想

假如同時要做兩件事:燒開水和煮粥

燒開水的步驟如下:

鍋子里放水,打開煤氣爐等待水燒開 // 阻塞關(guān)閉煤氣爐,把開水灌到水壺里

煮粥的步驟如下:

鍋子里放水和米,打開煤氣爐等待粥煮開 // 阻塞調(diào)整煤氣爐,改為小火等待粥煮熟 // 阻塞關(guān)閉煤氣爐

為了同時完成兩件事,一種方案是同時請兩個人分別做其中的一件事,這相當于采用多線程來同時完成多個任務(wù)。還有一種方案是讓一個人同時完成兩件事,這個人應(yīng)該善于利用一件事的空閑時間去做另一件事,這個人一刻也不應(yīng)該閑著:

鍋子里放水,打開煤氣爐 // 開始燒開水鍋子里放水和米,打開煤氣爐 // 開始煮粥while(一直等待,直到有水燒開、粥煮開或粥煮熟事件發(fā)生) { // 阻塞if(水燒開)關(guān)閉煤氣爐,把開水灌到水壺里;if((粥煮開)調(diào)整煤氣爐,改為小火;if(粥熟)關(guān)閉煤氣爐;}

這個人不斷監(jiān)控燒水和煮粥的狀態(tài),如果發(fā)生了條件中任一事件就去處理,處理完一件事后繼續(xù)監(jiān)控,直到所有的任務(wù)都完成

以上工作方式也可以被運用到服務(wù)器程序中,服務(wù)器程序只需要一個線程就能同時接收客戶的連接、接收各個客戶發(fā)送的數(shù)據(jù),以及向各個客戶發(fā)送響應(yīng)數(shù)據(jù)。服務(wù)器程序的處理流程如下:

while(一直等待,直到有接收連接就緒事件、讀緒事件或?qū)懢途w事件發(fā)生) { //阻塞if(有客戶連接)接收客戶的連接; // 非阻塞if(某個socket的輸入流中有可讀數(shù)據(jù))從輸入流中讀數(shù)據(jù); // 非阻塞if(某個socket的輸出流可以寫數(shù)據(jù))向輸出流寫數(shù)據(jù); // 非阻塞}

以上處理流程采用了輪詢的工作方式,當某一種操作就緒,就執(zhí)行該操作,否則就查看是否還有其他就緒的操作可以執(zhí)行。線程不會因為某一個操作還沒有就緒,就進入阻塞狀態(tài),一直傻傻地在那里等待這個操作就緒

為了使輪詢的工作方式順利進行,接收客戶的連接、從輸入流讀數(shù)據(jù),以及向輸出流寫數(shù)據(jù)的操作都應(yīng)該以非阻寒的方式運行。所謂非阻塞,指當線程執(zhí)行這些方法時,如果操作還沒有就緒,就立即返回,而不會一直等到操作就緒

非阻塞通信 API

java.nio.channels包提供了支持非阻塞通信的類,如下所述:

ServerSocketChannelServerSocket的替代類,支持阻塞通信與非阻塞通信SocketChannelSocket的替代類,支持阻塞通信與非阻塞通信Selector:為 ServerSocketChannel監(jiān)控接收連接就緒事件,為 SocketChannel監(jiān)控連接就緒、讀就緒和寫就緒事件SelectionKey:代表 ServerSocketChannel以及 SocketChannelSelector注冊事件的句柄。當一個 SelectionKey對象位于 Selector對象的 selected-keys集合中,就表示與這個 SelectionKey對象相關(guān)的事件發(fā)生了

ServerSocketChannelSocketChannel都是 SelectableChannel的子類,如圖所示,SelectableChannel類及其子類都能委托 Selector來監(jiān)控它們可能發(fā)生的一些事件,這種委托過程也被稱為注冊事件過程

ServerSocketChannelSelector注冊接收連接就緒事件的代碼如下:

SelectionKey key = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

SelectionKey類的一些靜態(tài)常量表示事件類型,ServerSockerChamnel只可能發(fā)生一種事件:

SelectionKey.OP_ACCEPT:接收連接緒事件,表示至少有了一個客戶連接,服務(wù)器可以接收這個連接、

SocketChannel可能發(fā)生以下三種事件:

SelectionKey.OP_CONNECT:連接就緒事件,表示客戶與服務(wù)器的連接已經(jīng)建立成功SelectionKey.OP_READ:讀就緒事件,表示輸入流中已經(jīng)有了可讀數(shù)據(jù),可以執(zhí)行讀操作了SelectionKey.OP_WRITE: 寫就緒事件,表示已經(jīng)可以向輸出流寫數(shù)據(jù)了

SocketChannel提供了接收和發(fā)送數(shù)據(jù)的方法:

read(ByteBuffer buffer):接收數(shù)據(jù),把它們存放到參數(shù)指定的 ByteBufferwrite(ByteBuffer buffer):把參數(shù)指定的 ByteBuffer 中的數(shù)據(jù)發(fā)送出去

ByteBuffer表示字節(jié)緩沖區(qū),SocketChannelread()write()方法都會操縱 ByteBufferByteBuffer類繼承于 Buffer類。ByteBuffer中存放的是字節(jié),為了把它們轉(zhuǎn)換為字符串還需要用到 Charset類,Charset類代表字符編碼,它提供了把字節(jié)流轉(zhuǎn)換為字符串(解碼過程)和把字符串轉(zhuǎn)換為字節(jié)流(編碼過程)的實用方法

下面分別介紹 Buffer、CharsetSelectableChannel、ServerSocketChannel、SocketChannel、SelectorSelectionKey的用法

緩沖區(qū) Buffer

數(shù)據(jù)輸入和輸出往往是比較耗時的操作,緩沖區(qū)從兩個方面提高 I/O 操作的效率:

減少實際的物理讀寫次數(shù)緩沖區(qū)在創(chuàng)建時被分配內(nèi)存,這塊內(nèi)存區(qū)域一直被重用,這可以減少動態(tài)分配和回收內(nèi)存區(qū)域的次數(shù)

java.nio包公開了 Buffer類的 API,使得 Java 程序可以直接控制和運用緩沖區(qū),所有的緩沖區(qū)都有以下屬性:

容量(capacity):表示緩沖區(qū)可以保存多少數(shù)據(jù)極限(limit):表示緩沖區(qū)的當前終點,不能對緩沖區(qū)中超過極限的區(qū)域進行讀寫操作位置(position):表示緩沖區(qū)中下一個讀寫單元的位置

以上三個屬性的關(guān)系為:容量 > 極限 >= 位置 >= 0

緩沖區(qū)提供了用于改變以上三個屬性的方法:

// 把極限設(shè)為容量,把位置設(shè)為0clear();// 把極限設(shè)為位置,把位置設(shè)為 0flip();// 不改變極限,把位置設(shè)為0rewind();

Buffer類的 remaining()方法返回緩沖區(qū)的剩余容量,取值等于 極限 - 位置

Buffer類的 compact()方法刪除緩沖區(qū)內(nèi)從 0 到當前位置 position 的內(nèi)容,然后把從當前位置 position 到極限limit 的內(nèi)容拷貝到 0 到 limit - position 的區(qū)域內(nèi)

java.nio.Buffer類是一個抽象類,不能被實例化。它共有 8 個具體的緩沖區(qū)類,其中最基本的緩沖區(qū)是 ByteBuffer,它存放的數(shù)據(jù)單元是字節(jié),ByteBufer類并沒有提供公開的構(gòu)造方法,但是提供了兩個獲得 ByteBuffer實例的靜態(tài)工廠方法:

// 返回一個ByteBuffer對象,參數(shù)capacity指定緩沖區(qū)的容量allocate(int capacity);// 返回一個ByteBuffer對象,參數(shù)capacity指定緩沖區(qū)的容量// 該方法返回的緩沖區(qū)被稱為直接緩沖區(qū),能進一步提高 I/O 操作的速度// 分配直接緩沖區(qū)的系統(tǒng)開銷很大,因此只有在緩沖區(qū)較大并且長期存在,或經(jīng)常重用時,才使用該緩沖區(qū)directAllocate(int capacity);

除 boolean 類型以外,每種基本類型都有對應(yīng)的緩沖區(qū)類,包括 CharBuffer,DoubleBuffer,FloatBuffer,IntBufferLongBuffer,ShortBuffer。在 CharBuffer中存放的數(shù)據(jù)單元為字符,以此類推。還有一種緩沖區(qū)是 MappedByteBuffer,它是 ByteBuffer的子類,能夠把緩沖區(qū)和文件的某個區(qū)域直接映射

所有具體緩沖區(qū)類都提供了讀寫緩沖區(qū)的方法:

// 相對讀,從緩沖區(qū)的當前位置讀取一個單元的數(shù)據(jù),讀完后把位置加1get();// 絕對讀,從參數(shù) index 指定的位置讀取一個單元的數(shù)據(jù)get(int index);// 相對寫,向緩沖區(qū)的當前位置寫一個單元的數(shù)據(jù),寫完后把位置加1put(單元數(shù)據(jù)類型 data);// 絕對寫,向參數(shù)index指定的位置寫入一個單元的數(shù)據(jù)put(int index, 單元數(shù)據(jù)類型 data);

ByteBuffer類不僅可以讀取和寫入一個單元的字節(jié),還可以讀取和寫入 int、char、float 和 double 等基本類型的數(shù)據(jù),例如:

getInt()getInt(int index)

以上不帶 index 參數(shù)的方法會在當前位置讀取或?qū)懭霐?shù)據(jù),稱為相對讀寫。帶 index 參數(shù)的方法會在 index 參數(shù)指定的位置讀取或?qū)懭霐?shù)據(jù),稱為絕對讀寫

字符編碼 Charset

java.nio.Charset類的每個實例代表特定的字符編碼類型,把字節(jié)序列轉(zhuǎn)換為字符串的過程稱為解碼,把字符串轉(zhuǎn)換為字節(jié)序列的過程稱為編碼

Charset類提供了編碼與解碼的方法:

// 對參數(shù)str指定的字符串進行編碼,把得到的字節(jié)序列存放在一個ByteBuffer對象并將其返回ByteBuffer encode(String str);// 對參數(shù)cb指定的字符緩沖區(qū)中的字符進行編碼,把得到的字節(jié)序列存放在一個ByteBuffer對象并將其返回ByteBuffer encode(CharBuffer cb);// 對參數(shù)bb指定的ByteBuffer的字節(jié)序列進行解碼,把得到的字符序列存放在一個CharBuffer對象并將其返回CharBuffer decode(ByteBuffer bb);

Charset類的靜態(tài) forName(String encode)方法返回一個 Charset對象,參數(shù) encode指定編碼類型。例如以下代碼創(chuàng)建了一個代表 GBK 編碼的 Charset對象

Charset charset = Charset.forName("GBK");

Charset類還有一個靜態(tài)方法 defaultCharset(),它返回代表本地平臺的默認字符編碼的 Charset對象

通道 Channel

通道(Channel)用來連接緩沖區(qū)與數(shù)據(jù)源或數(shù)據(jù)匯(即數(shù)據(jù)目的地),數(shù)據(jù)源的數(shù)據(jù)經(jīng)過通道到達緩沖區(qū),緩沖區(qū)的數(shù)據(jù)經(jīng)過通道到達數(shù)據(jù)匯

Channel 的主要層次結(jié)構(gòu)如下:

java.nio.channels.Channel接口只聲明了兩個方法:

// 關(guān)閉通道close();// 判斷通道是否打開isOpen();

Channel接口的兩個最重要的子接口是 ReadableByteChannelWritableByteChannelReadableByteChannel接口聲明了 read(ByteBuffer dst)方法,該方法把數(shù)據(jù)源的數(shù)據(jù)讀入?yún)?shù)指定的 ByteBuffer緩沖區(qū)中。WritableByteChannel接口聲明了 write(ByteBuffer src)方法,該方法把參數(shù)指定的 ByteBuffer緩沖區(qū)中的數(shù)據(jù)寫到數(shù)據(jù)匯中

ByteChannel接口是一個便利接口,它擴展了 ReadableByteChannelWritableByteChannel接口,因而同時支持讀寫操作

ScatteringByteChannel接口擴展了 ReadableByteChannel接口,允許分散地讀取數(shù)據(jù)。分散讀取數(shù)據(jù)指單個讀取操作能填充多個緩沖區(qū),ScatteringByteChannel接口聲明了 read(ByteBuffer[] dsts)方法,該方法把從數(shù)據(jù)源讀取的數(shù)據(jù)依次填充到參數(shù)指定的各個 ByteBuffer

GatheringByteChannel擴展了 WritableByteChannel接口,允許集中地寫入數(shù)據(jù)。集中寫入數(shù)據(jù)指單個寫操作能把多個緩沖區(qū)的數(shù)據(jù)寫到數(shù)據(jù), GatheringByteChannel接口聲明了 write(ByteBuffer[] srcs)方法,該方法依次把參數(shù)指定的每個 ByteBuffer中的數(shù)寫到數(shù)據(jù)匯

FileChannel類是 Channel接口的實現(xiàn)類,代表一個與文件相連的通道。該類實現(xiàn)了 ByteChannel、ScatteringByteChannelGatheringByteChannel接口,支持讀操作、寫操作、分散讀操作和集中寫操作。FileChannel類沒有提供公開的構(gòu)造方法,因此不能用 new語句來構(gòu)造它的實例。不過,在FileInputStream、FileOutputStreamRandomAccessFile類中提供了 getChannel()方法,該方法返回相應(yīng)的 FileChannel對象

SelectableChannel也是一種通道,它不僅支持阻塞的 I/O操作,還支持非阻塞的 I/OSelectableChannel有兩個子類,ServerSocketChannelSocketChannelSocketChannel還實現(xiàn)了 ByteChannel接口,具有 read(ByteBuffer dst)write(ByteBuffer src)方法

1. SelectableChannel 類

SelectableChannel是一種支持阻塞 IO 和非阻塞 IO 的通道。在非阻塞模式下,讀寫數(shù)據(jù)不會阻塞,并且 SelectableChannel可以向 Selector注冊讀就緒和寫就緒等事件。Selector負責監(jiān)控這些事件,等到事件發(fā)生時,比如發(fā)生了讀就緒事件,SelectableChannel就可以執(zhí)行讀操作了

SelectableChannel的主要方法如下:

// 當參數(shù)block為true,表示把SelectableChannel設(shè)為阻塞模式// 當參數(shù)block為false時,表示把SelectableChannel設(shè)為非阻塞模式// SelectableChannel默認采用阻塞模式// 該方法返回SelectableChannel對象本身的引用,相當于return thispublic SelectableChannel configureBlocking(boolean block) throws IOException// 以下兩個方法都向Selector注冊事件public SelectionKey register(Selector sel,int ops) throws ClosedChannelExceptionpublic SelectionKey register(Selector sel,int ops,Object attachment) throws ClosedChannelException

以下是 socketChannelSelector注冊讀就緒和寫就緒事件

SelectionKey key = socketChannel.register(selector.SelectionKey.OP_READ | SelectionKey.OP_WRITE);

register()方法返回一個 SelectionKey對象,SeletionKey被用來跟蹤被注冊的事件。第二個 register()方法還有一個 Object類型的參數(shù) attachment,用于為 SelectionKey關(guān)聯(lián)附件,當被注冊事件發(fā)生后,需要處理該事件時,可以從 SelectionKey中獲得這個附件,該附件可用來包含與處理這個事件相關(guān)的信息

2. ServerSocketChannel 類

ServerSocketChannel繼承自 SelectableChannel,是 ServerSocket的替代類,通過它的靜態(tài)方法 open()來創(chuàng)建。每個 ServerSockeChannel對象都與一個 ServerSocket對象關(guān),通過 socket()方法返回與它關(guān)聯(lián)的 ServerSocket對象??赏ㄟ^以下方式把服務(wù)器進程綁定到一個本地端口:

serverSocketChannel.socket().bind(port);

ServerSocketChannel的主要方法如下:

// 返回一個ServerSocketChannel對象,該對象沒有與任何本地端口綁定,并且處于阻塞模式public static ServerSocketChannel open() throws IOException// 用于接收客戶的連接,如果處于非阻塞狀態(tài),當沒有客戶連接時就立即返回nullpublic SocketChannel accept() throws IOException// 返回ServerSocketChannel所能產(chǎn)生的事件,這個方法總是返回SelectionKey.OP_ACCEPTpublic final int validOps()// 返回ServerSocketChannel關(guān)聯(lián)的ServerSocket對象public ServerSocket socket()
3. SocketChannel類

SockeChannel可以被看作是 Socket的替代類,SockeChannel不僅繼承了 SelectableChannel,而且實現(xiàn)了 ByteChannel。SockeChannel同樣通過它的靜態(tài)方法 open()來創(chuàng)建

public static SocketChannel open() throws IOException// 帶參數(shù)的構(gòu)造方法還會建立與遠程服務(wù)器的連接public static SocketChannel open(SocketAddress remote) throws IOException

SocketChannel的主要方法如下:

// 返回ServerSocketChannel所能產(chǎn)生的事件,這個方法總是返回以下值// SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITEpublic final int validOps()// 返回SocketChannel關(guān)聯(lián)的Socket對象public Socket socket()// 建立遠程連接,當處于非阻塞模式時,如果立即連接成功返回true,否則返回falsepublic boolean connect(SocketAddress remote) throws IOException// 判斷底層Socket是否已經(jīng)建立遠程連接public boolean isConnected()// 判斷是否正在進行遠程連接,如果遠程連接操作已經(jīng)開始,但還沒有完成,則返回true,否則返回false// 也就是說,無論底層Socket還沒有開始連接,或者已經(jīng)連接成功,該方法都會返回falsepublic boolean isConnectionPending()// 試圖完成連接遠程服務(wù)器的操作// 非阻塞模式下,建立連接從調(diào)用connect()方法開始,到調(diào)用finishConnect()方法結(jié)束// 如果在調(diào)用此方法之前連接已經(jīng)建立,則立即返回true,否則立即返回false// 阻塞模式下,如果連接操作還沒有完成,則會進入阻塞狀態(tài),直到連接完成,或者出現(xiàn)IO異常public boolean finishConnect) throws IOException// 從Channel讀入若干字節(jié),存放到參數(shù)指定的ByteBuffer// 假設(shè)ByteBuffer剩余容量為r,阻塞模式下,該方法會爭取讀到r字節(jié)// 如果輸入流中不足r字節(jié),就進入阻塞狀態(tài),直到讀入了r字節(jié),或者讀到了輸入流末尾,或者出現(xiàn)了IO異常// 非阻塞模式下,該方法奉行能讀到多少數(shù)據(jù)就讀多少數(shù)據(jù)的原則// 通道中的可讀數(shù)據(jù),有可能不足r字節(jié),或者為0字節(jié),總是立即返回// 該方法返回實際上讀入的字節(jié)數(shù),有可能為0,如果返回-1,表示讀到了輸入流的末尾public int read(ByteBuffer dst) throws IOException// 把參數(shù)src指定的ByteBuffer的字節(jié)寫到Channel// 假設(shè)ByteBuffer剩余容量為r,阻塞模式下,該方法會爭取輸出r字節(jié)// 如果底層網(wǎng)絡(luò)的輸出緩沖區(qū)不能容納r字節(jié),就進入阻塞狀態(tài),直到輸出了r字節(jié),或者出現(xiàn)了IO異常// 非阻塞模式下,該方法奉行能輸出多少數(shù)據(jù)就輸出多少數(shù)據(jù)的原則,有可能不足r字節(jié),或者為0字節(jié),總是立即返回// 該方法返回實際上輸出的字節(jié),有可能為0public int write(ByteBuffer src) throws IOException
Selector 類

只要 ServerSockerChannel以及 SocketChannelSelector注冊了特定的事件,Selector就會監(jiān)控這些事件是否發(fā)生。SelectableChannelregister()方法負責注冊事件,該方法返回 SelectionKey對象,該對象是用于跟蹤這些被注冊事件的句柄

Selector對象中會包含三種類型的 SelectionKey的集合:

all-keys:當前所有向 Selector注冊的 SelectionKey的集合,Selectorkeys()方法返回該集合selected-keys:相關(guān)事件已經(jīng)被 Selector捕獲的 SelectionKey的集合,SelectorselectedKeys()方法返回該集合cancelled-keys:已經(jīng)被取消的 SelectionKey的集合,Selector沒有提供訪問這種集合的方法

當執(zhí)行 SelectableChannelregiste()方法,會新建一個 SelectionKey并加入 Selectorall-keys集合中。如果關(guān)閉了與 SelectionKey對象關(guān)聯(lián)的 Channel對象,或者調(diào)用了 SelectionKey對象的 cancel()方法,那么這個 SelectionKey對象就會被加入 cancelled-keys集合,表示已經(jīng)被取消,在程序下一次執(zhí)行 Selectorselect()方法時,被取消的 SelectionKey對象將從所有的集合(包括 all-keys集合、selected-keys集合和 cancelled-keys集合)中被刪除

在執(zhí)行 Selectorselect()方法時,如果與 SelectionKey相關(guān)的事件發(fā)生了,這個 SelectionKey就被加入 selected-keys集合中。程序直接調(diào)用 selected-keys集合的 remove()方法,或者調(diào)用它的 Iteratorremove()方法,都可以從 selected-keys集合中刪除一個 SelectionKey對象

程序不允許直接通過集合接口的 remove()方法刪除 all-keys集合中的 SelectionKey對象,這會導(dǎo)致 UnsupportedOperationException

Selector類的主要方法如下:

// Selector的靜態(tài)工廠方法,創(chuàng)建一個Selector對象public static Selector open() throws IOException// 判斷Selector是否處于打開狀態(tài),Selector對象創(chuàng)建后就處于打開狀態(tài),當調(diào)用close()方法就進入關(guān)閉狀態(tài)public boolean isOpen()// 返回Seleclor的all-keys集合,包含了所有與Seclector關(guān)聯(lián)的SelectionKey對象public Set keys()// 返回相關(guān)事件已經(jīng)發(fā)生的SelectionKey對象的數(shù)目// 該方法采用非阻塞的工作方式,返回當前相關(guān)事件已經(jīng)發(fā)生的SelectionKey對象的數(shù)目,如果沒有,就立即返回0public int selectNow() throws IOException// 返回相關(guān)事件已經(jīng)發(fā)生的SelectionKey對象的數(shù)目// 該方法采用阻塞的工作方式,如果一個也沒有,就進入阻塞狀態(tài),直到出現(xiàn)以下情況之一,就會從select()返回:// 1.至少有一個SelectionKey的相關(guān)事件已經(jīng)發(fā)生// 2.其他線程調(diào)用了Selector的wakeup()方法// 3.當前執(zhí)行select()方法的線程被其他線程中斷// 4.超出了等待時間public int select() throws IOExceptionpublic int select(long timeout) throws IOException// 喚醒執(zhí)行Selector的select()方法 public Selector wakeup()// 關(guān)閉 Selector// 如果有其他線程正執(zhí)行這個Selector的select()方法并且處于阻塞狀態(tài),這個線程會立即返回// close()方法使得Selector占用的所有資源都被釋敗,所有關(guān)聯(lián)的SelectionKey都被取消public void close() throws IOException
SelectionKey 類

SelectionKey中定義了四種事件,分別用四個 int 類型的常量來表示:

SelectionKey.OP_ACCEPT:接收連接就緒事件,表示服務(wù)器監(jiān)聽到了客戶連接,服務(wù)器可以接收這個連接了,常量值為 16SeiectionKey.OP_CONNECT:連接就緒事件表示客戶與服務(wù)器的連接已經(jīng)建立成功,常量值為 8SelectionKey.OP_READ:讀就緒事件,表示通道中已經(jīng)有了可讀數(shù)據(jù),可以執(zhí)行讀操作了,常量值為 1SelectionKey.OP_WRITE:寫就緒事件表示已經(jīng)可以向通道寫數(shù)據(jù)了,常量值為 4

以上常量分別占據(jù)不同的二進制位,因此可以通過二進制的或運算來將它們進行任意組合

一個 SelectionKey對象中包含兩種類型的事件:

所有感興趣的事件:通過 SelectableChannelregister()方法注冊事件時,可以在參數(shù)中指定 SelectionKey感興趣的事件

SelectionKey key = socketChannel.register(selector,SelectionKey.OP_CONNECT | SelectionKey.OP_READ);

該代碼表示這個 SelectionKey對讀就緒和寫就緒事件感興趣,與之關(guān)聯(lián)的 Selector對象會負責監(jiān)控這些事件

SelectionKey的帶參數(shù)的 interestOps(int ops)方法也可以為 SelectionKey對象增加一個感興趣的事件,如下代碼所示:

key.interestOps(SelectionKey.OP_WRITE);

所有已經(jīng)發(fā)生的事件:SeletionKeyreadyOps()方法返回所有已經(jīng)發(fā)生的事件,例如假定返回值為 SelectionKey.OP_WRITE | SelectionKey.OP_READ,表示讀就緒和寫就緒事件已經(jīng)發(fā)生了,這意味著與之關(guān)聯(lián)的 SocketChannel對象可以進行讀操作和寫操作了

SelectionKey的主要方法如下:

// 返回與這個SelectionKey對象關(guān)聯(lián)的SelectableChannel對象public SelectableChannel channel()// 返回與這個SelectionKey對象關(guān)聯(lián)的Selector對象public Selector selector()// 判斷這個SelectionKey是否有效// 當SelectionKey對象創(chuàng)建后,它就一直處于有效狀態(tài)// 如果調(diào)用了它的cancel()方法,或關(guān)閉了與它關(guān)聯(lián)的SelectableChannel或Selector對象,它就失效public boolean isValid()// 使SelectionKey對象失效public void cancel()// 返回這個SelectionKey感興趣的事件public int interestOps()// 為SelectionKey增加感興趣的事件public SelectionKey interestOps(int ops)// 返回已經(jīng)就緒的事件public int readyOps()// 判斯與之關(guān)聯(lián)的SocketChannel的讀就緒事件是否已經(jīng)發(fā)生public final boolean isReadable()// 判斷與之關(guān)聯(lián)的SocketChannel的寫就緒事件是否已經(jīng)發(fā)生public final boolean isWritable()// 判斷與之關(guān)聯(lián)的SocketChannel的連接就緒事件是否已經(jīng)發(fā)生public final boolean isConnectable()// 判斷與之關(guān)聯(lián)的ServerSocketChannel的接收連接就緒事件是否已經(jīng)發(fā)生public final boolean isAcceptable()// 使SelectionKey關(guān)聯(lián)一個附件,一個SelectionKey對象只能關(guān)聯(lián)一個Object類型的附件// 如果多次調(diào)用該方法,則只有最后一個附件與SelectionKey對象關(guān)聯(lián)public final Object attach(Object obj)// 返回與SelectionKey對象關(guān)聯(lián)的附件public final Object attachment()
Channels 類

Channels類是一個簡單的工具類,提供了通道與傳統(tǒng)的基于 IO 的流、ReaderWriter之間進行轉(zhuǎn)換的靜態(tài)方法

ReadableByteChannel newChannel(InputStream in) // 輸入流轉(zhuǎn)換成讀通道WritableByteChannel newChannel(OutputStream out) // 輸出流轉(zhuǎn)換成寫通道InputStream newInputStream(AsynchronousByteChannel ch) // 異步通道轉(zhuǎn)換成輸入流InputStream newInputStream(ReadableByteChannel ch) // 讀通道轉(zhuǎn)換成輸入流OutputStream newOutputStream(AsynchronousByteChannel ch) // 異步通道轉(zhuǎn)換成輸出流OutputStream newOutputStream(WritableByteChannel ch) // 寫通道轉(zhuǎn)換成輸出流Reader newReader(ReadableByteChannel ch,String csName) // 讀通道轉(zhuǎn)換成Reader,參數(shù)csName指定字符編碼Reader newReader(ReadableByteChannel ch,Charset charset)//讀通道轉(zhuǎn)換成Reader.參數(shù)charset指定字符編碼Reader newReader(ReadableByteChannel ch,CharsetDecoder dec, int minBufferCap) // 讀通道轉(zhuǎn)換成 Reader,參數(shù)dec指定字符解碼器,參數(shù)minBufferCap指定內(nèi)部字節(jié)緩沖區(qū)的最小容量Writer newWriter(WritableByeChannel ch, String csName) // 寫通道轉(zhuǎn)換Writer.參數(shù)csName指定字符編碼Writer newWriter(WritableByeChannel ch, Charset charset) // / 寫通道轉(zhuǎn)換Writer.參數(shù)charset指定字符編碼Writer newWriter(WritableByeChannel ch, CharsetEncoder enc, int minBufferCap) // 寫通道轉(zhuǎn)換成Writer,參數(shù)dec指定字符解碼器,參數(shù)minBufferCap指定內(nèi)部字節(jié)緩沖區(qū)的最小容量
Socket 選項

從 JDK7 開始,SocketChannel、ServerSocketChannel、AsynchronousSocketChannelAsynchronousServerSocketChannelDatagramChannel都實現(xiàn)了新的 NetworkChannel接口。NetworkChannel接口的主要作用是設(shè)置和讀取各種 Socket 選項

NetworkChannel接口提供了用于設(shè)置和讀取這些選項的方法:

 T getOption(SocketOption name) // 獲取特定的Socket選項值 NetworkChannel setOption(SocketOption name, T value) // 設(shè)置特定的Socket選項Set> supportedOptions() // 獲取所有支持的Socket選項

SocketOptionl類是一個泛型類,SocketOption中的 T代表特定選項的取值類型,可選值包括 Integer、BooleanNetworkInterface

StandardSocketOptions類提供了以下表示特定選項的常量:

SocketOption  --  StandardSocketOptions.IP_MULTICAST_IFSocketOption  --  StandardSocketOptions.IP_MULTICAST_LOOPSocketOption  --  StandardSocketOptions.IP_MULTICAST_TTLSocketOption  --  StandardSocketOptions.IP_TOSSocketOption  --  StandardSocketOptions.SO_BROADCASTSocketOption  --  StandardSocketOptions.SO_KEEPALIVESocketOption  --  StandardSocketOptions.SO_LINGERSocketOption  --  StandardSocketOptions.SO_RCVBUFSocketOption  --  StandardSocketOptions.SO_REUSEADDRSocketOption  --  StandardSocketOptions.SO_REUSEPORTSocketOption  --  StandardSocketOptions.SO_SNDBUFSocketOption  --  StandardSocketOptions.TCP_NODELAY

關(guān)鍵詞:

相關(guān)閱讀

泰州市| 海丰县| 南郑县| 临邑县| 沭阳县| 峨眉山市| 北京市| 阳谷县| 沙洋县| 金昌市| 和平县| 密云县| 神农架林区| 盐源县| 宜君县| 广灵县| 阳高县| 雅安市| 德江县| 江津市| 沧州市| 临海市| 绥滨县| 泽普县| 桓仁| 南安市| 尖扎县| 丘北县| 堆龙德庆县| 酒泉市| 五常市| 珠海市| 蓬安县| 海口市| 莱州市| 济阳县| 邓州市| 蒲江县| 扶绥县| 平泉县| 山东|