執行緒阻塞概述
在生活中,最常見的阻塞現象是公路上汽車的堵塞,汽車在公路上快速行駛,如果前方交通受阻,就只好停下來等待,等到公路順暢,才能恢復行駛,
執行緒在運行中也會因為某些原因而阻塞,所有處于阻塞狀態的執行緒的共同特征:放棄 CPU,暫停運行,只有等到導致阻塞的原因消除,才能恢復運行,或者被其他執行緒中斷該執行緒會退出阻塞狀態,并且拋出 InterruptedException
導致執行緒阻塞的原因主要有以下方面:
- 執行緒執行了
Threadsleep(int n)方法,執行緒放棄 CPU,睡眠 n ms,然后恢復運行 - 執行緒要執行一段同步代碼,由于無法獲得相關的同步鎖,只好進入阻塞狀態,等到獲取同步鎖再恢復運行
- 執行緒執行了一個物件的
wait()方法,進入阻塞狀態,只有等到其他執行緒執行了該物件的notify()或notifyAll()方法,才可能將其喚醒 - 執行緒執行 IO 操作或進行遠程通信時,會因為等待相關的資源而進入阻塞狀態
進行遠程通信時,在客戶程式中,執行緒在以下情況下可能進入阻塞狀態:
-
請求與服務器建立連接時,即當執行緒執行 Socket 的帶引數的構造方法,或執行 Socke 的
connect()方法時,會進入阻塞狀態,直到連接成功,此執行緒才從 Socket 的構造方法或connect()方法回傳 -
執行緒從 Socket 的輸入流讀入資料時,如果沒有足夠的資料,就會進入阻塞狀態,直到讀到了足夠的資料,或者到達輸入流的末尾,或者出現了例外,才從輸入流的
read()方法回傳或例外中斷輸入流中有多少資料才算足夠呢?這要看執行緒執行的
read()方法的類:int read():只要輸入流中有 1 位元組,就算足夠int read(byte[] buf):只要輸入流中的位元組數目與引數 buff 陣列的長度相同,就算足夠String readLine():只要輸入流中有 1 行字符,就算足夠
-
執行緒向 Socket 的輸出流寫一批資料時,可能會進入阻塞狀態,等到輸出了所有的資料,或者出現例外,才從輸出流的
write()方法回傳或例外中斷 -
如果呼叫 Socket 的
setSoLinger()方法設定了關閉 Socket 的延遲時間,那么當執行緒執行 Socket 的close()方法時,會進入阻塞狀態,直到底層 Socket 發送完所有剩余資料或者超過了setSoLinger()方法設定的延遲時間,才從close()方法回傳
在服務器程式中,執行緒在以下情況下可能會進入阻塞狀態:
- 執行緒執行 ServerSocket 的
accept()方法,等待客戶的連接,直到接收到了客戶連接才從accept()方法回傳 - 執行緒從 Socket 的輸入流讀入資料時,如果輸入流沒有足夠的資料就會進入阻塞狀態
- 執行緒向 Socket 的輸出流寫一批資料時,可能會進入阻塞狀態,等到輸出了所有的資料,或者出現例外,才從輸出流的
write()方法回傳或例外中斷
由此可見,無論是在服務器程式還是客戶程式中,當通過 Socket 的輸入流和輸出流讀寫資料時,都可能進入阻塞狀態,這種可能出現阻塞的輸入和輸出操作被稱為阻塞 IO,與此對照,如果執行輸入和輸出操作時,不會發生阻塞,則稱為非阻塞 IO
非阻塞通信的基本思想
假如同時要做兩件事:燒開水和煮粥
燒開水的步驟如下:
鍋子里放水,打開煤氣爐
等待水燒開 // 阻塞
關閉煤氣爐,把開水灌到水壺里
煮粥的步驟如下:
鍋子里放水和米,打開煤氣爐
等待粥煮開 // 阻塞
調整煤氣爐,改為小火
等待粥煮熟 // 阻塞
關閉煤氣爐
為了同時完成兩件事,一種方案是同時請兩個人分別做其中的一件事,這相當于采用多執行緒來同時完成多個任務,還有一種方案是讓一個人同時完成兩件事,這個人應該善于利用一件事的空閑時間去做另一件事,這個人一刻也不應該閑著:
鍋子里放水,打開煤氣爐 // 開始燒開水
鍋子里放水和米,打開煤氣爐 // 開始煮粥
while(一直等待,直到有水燒開、粥煮開或粥煮熟事件發生) { // 阻塞
if(水燒開)
關閉煤氣爐,把開水灌到水壺里;
if((粥煮開)
調整煤氣爐,改為小火;
if(粥熟)
關閉煤氣爐;
}
這個人不斷監控燒水和煮粥的狀態,如果發生了條件中任一事件就去處理,處理完一件事后繼續監控,直到所有的任務都完成
以上作業方式也可以被運用到服務器程式中,服務器程式只需要一個執行緒就能同時接收客戶的連接、接收各個客戶發送的資料,以及向各個客戶發送回應資料,服務器程式的處理流程如下:
while(一直等待,直到有接收連接就緒事件、讀緒事件或寫就緒事件發生) { //阻塞
if(有客戶連接)
接收客戶的連接; // 非阻塞
if(某個socket的輸入流中有可讀資料)
從輸入流中讀資料; // 非阻塞
if(某個socket的輸出流可以寫資料)
向輸出流寫資料; // 非阻塞
}
以上處理流程采用了輪詢的作業方式,當某一種操作就緒,就執行該操作,否則就查看是否還有其他就緒的操作可以執行,執行緒不會因為某一個操作還沒有就緒,就進入阻塞狀態,一直傻傻地在那里等待這個操作就緒
為了使輪詢的作業方式順利進行,接收客戶的連接、從輸入流讀資料,以及向輸出流寫資料的操作都應該以非阻寒的方式運行,所謂非阻塞,指當執行緒執行這些方法時,如果操作還沒有就緒,就立即回傳,而不會一直等到操作就緒
非阻塞通信 API
java.nio.channels 包提供了支持非阻塞通信的類,如下所述:
ServerSocketChannel:ServerSocket的替代類,支持阻塞通信與非阻塞通信SocketChannel:Socket的替代類,支持阻塞通信與非阻塞通信Selector:為ServerSocketChannel監控接收連接就緒事件,為SocketChannel監控連
接就緒、讀就緒和寫就緒事件SelectionKey:代表ServerSocketChannel以及SocketChannel向Selector注冊事件的句柄,當一個SelectionKey物件位于Selector物件的selected-keys集合中,就表示與這個SelectionKey物件相關的事件發生了
ServerSocketChannel 及 SocketChannel 都是 SelectableChannel 的子類,如圖所示,SelectableChannel 類及其子類都能委托 Selector 來監控它們可能發生的一些事件,這種委托程序也被稱為注冊事件程序

ServerSocketChannel 向 Selector 注冊接收連接就緒事件的代碼如下:
SelectionKey key = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
SelectionKey 類的一些靜態常量表示事件型別,ServerSockerChamnel 只可能發生一種事件:
SelectionKey.OP_ACCEPT:接收連接緒事件,表示至少有了一個客戶連接,服務器可以接收這個連接、
SocketChannel 可能發生以下三種事件:
SelectionKey.OP_CONNECT:連接就緒事件,表示客戶與服務器的連接已經建立成功SelectionKey.OP_READ:讀就緒事件,表示輸入流中已經有了可讀資料,可以執行讀操作了SelectionKey.OP_WRITE: 寫就緒事件,表示已經可以向輸出流寫資料了
SocketChannel 提供了接收和發送資料的方法:
read(ByteBuffer buffer):接收資料,把它們存放到引數指定的 ByteBufferwrite(ByteBuffer buffer):把引數指定的 ByteBuffer 中的資料發送出去
ByteBuffer 表示位元組緩沖區,SocketChannel 的 read() 和 write() 方法都會操縱 ByteBuffer,ByteBuffer 類繼承于 Buffer 類,ByteBuffer 中存放的是位元組,為了把它們轉換為字串還需要用到 Charset 類,Charset 類代表字符編碼,它提供了把位元組流轉換為字串(解碼程序)和把字串轉換為位元組流(編碼程序)的實用方法
下面分別介紹 Buffer、Charset、SelectableChannel、ServerSocketChannel、SocketChannel、Selector 和 SelectionKey 的用法
緩沖區 Buffer
資料輸入和輸出往往是比較耗時的操作,緩沖區從兩個方面提高 I/O 操作的效率:
- 減少實際的物理讀寫次數
- 緩沖區在創建時被分配記憶體,這塊記憶體區域一直被重用,這可以減少動態分配和回收記憶體區域的次數
java.nio 包公開了 Buffer 類的 API,使得 Java 程式可以直接控制和運用緩沖區,所有的緩沖區都有以下屬性:
- 容量(capacity):表示緩沖區可以保存多少資料
- 極限(limit):表示緩沖區的當前終點,不能對緩沖區中超過極限的區域進行讀寫操作
- 位置(position):表示緩沖區中下一個讀寫單元的位置
以上三個屬性的關系為:容量 > 極限 >= 位置 >= 0
緩沖區提供了用于改變以上三個屬性的方法:
// 把極限設為容量,把位置設為0
clear();
// 把極限設為位置,把位置設為 0
flip();
// 不改變極限,把位置設為0
rewind();
Buffer 類的 remaining() 方法回傳緩沖區的剩余容量,取值等于 極限 - 位置
Buffer 類的 compact() 方法洗掉緩沖區內從 0 到當前位置 position 的內容,然后把從當前位置 position 到極限limit 的內容拷貝到 0 到 limit - position 的區域內
java.nio.Buffer 類是一個抽象類,不能被實體化,它共有 8 個具體的緩沖區類,其中最基本的緩沖區是 ByteBuffer,它存放的資料單元是位元組,ByteBufer 類并沒有提供公開的構造方法,但是提供了兩個獲得 ByteBuffer 實體的靜態工廠方法:
// 回傳一個ByteBuffer物件,引數capacity指定緩沖區的容量
allocate(int capacity);
// 回傳一個ByteBuffer物件,引數capacity指定緩沖區的容量
// 該方法回傳的緩沖區被稱為直接緩沖區,能進一步提高 I/O 操作的速度
// 分配直接緩沖區的系統開銷很大,因此只有在緩沖區較大并且長期存在,或經常重用時,才使用該緩沖區
directAllocate(int capacity);
除 boolean 型別以外,每種基本型別都有對應的緩沖區類,包括 CharBuffer,DoubleBuffer,FloatBuffer,IntBuffer,LongBuffer,ShortBuffer,在 CharBuffer 中存放的資料單元為字符,以此類推,還有一種緩沖區是 MappedByteBuffer,它是 ByteBuffer 的子類,能夠把緩沖區和檔案的某個區域直接映射
所有具體緩沖區類都提供了讀寫緩沖區的方法:
// 相對讀,從緩沖區的當前位置讀取一個單元的資料,讀完后把位置加1
get();
// 絕對讀,從引數 index 指定的位置讀取一個單元的資料
get(int index);
// 相對寫,向緩沖區的當前位置寫一個單元的資料,寫完后把位置加1
put(單元資料型別 data);
// 絕對寫,向引數index指定的位置寫入一個單元的資料
put(int index, 單元資料型別 data);
ByteBuffer 類不僅可以讀取和寫入一個單元的位元組,還可以讀取和寫入 int、char、float 和 double 等基本型別的資料,例如:
getInt()
getInt(int index)
以上不帶 index 引數的方法會在當前位置讀取或寫入資料,稱為相對讀寫,帶 index 引數的方法會在 index 引數指定的位置讀取或寫入資料,稱為絕對讀寫
字符編碼 Charset
java.nio.Charset 類的每個實體代表特定的字符編碼型別,把位元組序列轉換為字串的程序稱為解碼,把字串轉換為位元組序列的程序稱為編碼
Charset 類提供了編碼與解碼的方法:
// 對引數str指定的字串進行編碼,把得到的位元組序列存放在一個ByteBuffer物件并將其回傳
ByteBuffer encode(String str);
// 對引數cb指定的字符緩沖區中的字符進行編碼,把得到的位元組序列存放在一個ByteBuffer物件并將其回傳
ByteBuffer encode(CharBuffer cb);
// 對引數bb指定的ByteBuffer的位元組序列進行解碼,把得到的字符序列存放在一個CharBuffer物件并將其回傳
CharBuffer decode(ByteBuffer bb);
Charset 類的靜態 forName(String encode) 方法回傳一個 Charset 物件,引數 encode 指定編碼型別,例如以下代碼創建了一個代表 GBK 編碼的 Charset 物件
Charset charset = Charset.forName("GBK");
Charset 類還有一個靜態方法 defaultCharset(),它回傳代表本地平臺的默認字符編碼的 Charset 物件
通道 Channel
通道(Channel)用來連接緩沖區與資料源或資料匯(即資料目的地),資料源的資料經過通道到達緩沖區,緩沖區的資料經過通道到達資料匯
Channel 的主要層次結構如下:

java.nio.channels.Channel 介面只宣告了兩個方法:
// 關閉通道
close();
// 判斷通道是否打開
isOpen();
Channel 介面的兩個最重要的子介面是 ReadableByteChannel 和 WritableByteChannel,ReadableByteChannel 介面宣告了 read(ByteBuffer dst) 方法,該方法把資料源的資料讀入引數指定的 ByteBuffer 緩沖區中,WritableByteChannel 介面宣告了 write(ByteBuffer src) 方法,該方法把引數指定的 ByteBuffer 緩沖區中的資料寫到資料匯中
ByteChannel 介面是一個便利介面,它擴展了 ReadableByteChannel 和 WritableByteChannel 介面,因而同時支持讀寫操作
ScatteringByteChannel 介面擴展了 ReadableByteChannel 介面,允許分散地讀取資料,分散讀取資料指單個讀取操作能填充多個緩沖區,ScatteringByteChannel 介面宣告了 read(ByteBuffer[] dsts) 方法,該方法把從資料源讀取的資料依次填充到引數指定的各個 ByteBuffer
GatheringByteChannel 擴展了 WritableByteChannel 介面,允許集中地寫入資料,集中寫入資料指單個寫操作能把多個緩沖區的資料寫到資料, GatheringByteChannel 介面宣告了 write(ByteBuffer[] srcs) 方法,該方法依次把引數指定的每個 ByteBuffer 中的數寫到資料匯
FileChannel 類是 Channel 介面的實作類,代表一個與檔案相連的通道,該類實作了 ByteChannel、ScatteringByteChannel 和 GatheringByteChannel 介面,支持讀操作、寫操作、分散讀操作和集中寫操作,FileChannel 類沒有提供公開的構造方法,因此不能用 new 陳述句來構造它的實體,不過,在FileInputStream、FileOutputStream 和 RandomAccessFile 類中提供了 getChannel() 方法,該方法回傳相應的 FileChannel 物件
SelectableChannel 也是一種通道,它不僅支持阻塞的 I/O 操作,還支持非阻塞的 I/O,SelectableChannel 有兩個子類,ServerSocketChannel 和 SocketChannel,SocketChannel 還實作了 ByteChannel 介面,具有 read(ByteBuffer dst) 和 write(ByteBuffer src) 方法
1. SelectableChannel 類
SelectableChannel 是一種支持阻塞 IO 和非阻塞 IO 的通道,在非阻塞模式下,讀寫資料不會阻塞,并且 SelectableChannel 可以向 Selector 注冊讀就緒和寫就緒等事件,Selector 負責監控這些事件,等到事件發生時,比如發生了讀就緒事件,SelectableChannel 就可以執行讀操作了
SelectableChannel 的主要方法如下:
// 當引數block為true,表示把SelectableChannel設為阻塞模式
// 當引數block為false時,表示把SelectableChannel設為非阻塞模式
// SelectableChannel默認采用阻塞模式
// 該方法回傳SelectableChannel物件本身的參考,相當于return this
public SelectableChannel configureBlocking(boolean block) throws IOException
// 以下兩個方法都向Selector注冊事件
public SelectionKey register(Selector sel,int ops) throws ClosedChannelException
public SelectionKey register(Selector sel,int ops,Object attachment) throws ClosedChannelException
以下是 socketChannel 向 Selector 注冊讀就緒和寫就緒事件
SelectionKey key = socketChannel.register(selector.SelectionKey.OP_READ | SelectionKey.OP_WRITE);
register() 方法回傳一個 SelectionKey 物件,SeletionKey 被用來跟蹤被注冊的事件,第二個 register() 方法還有一個 Object 型別的引數 attachment,用于為 SelectionKey 關聯附件,當被注冊事件發生后,需要處理該事件時,可以從 SelectionKey 中獲得這個附件,該附件可用來包含與處理這個事件相關的資訊
2. ServerSocketChannel 類
ServerSocketChannel 繼承自 SelectableChannel,是 ServerSocket 的替代類,通過它的靜態方法 open() 來創建,每個 ServerSockeChannel 物件都與一個 ServerSocket 物件關,通過 socket() 方法回傳與它關聯的 ServerSocket 物件,可通過以下方式把服務器行程系結到一個本地埠:
serverSocketChannel.socket().bind(port);
ServerSocketChannel 的主要方法如下:
// 回傳一個ServerSocketChannel物件,該物件沒有與任何本地埠系結,并且處于阻塞模式
public static ServerSocketChannel open() throws IOException
// 用于接收客戶的連接,如果處于非阻塞狀態,當沒有客戶連接時就立即回傳null
public SocketChannel accept() throws IOException
// 回傳ServerSocketChannel所能產生的事件,這個方法總是回傳SelectionKey.OP_ACCEPT
public final int validOps()
// 回傳ServerSocketChannel關聯的ServerSocket物件
public ServerSocket socket()
3. SocketChannel類
SockeChannel 可以被看作是 Socket 的替代類,SockeChannel 不僅繼承了 SelectableChannel,而且實作了 ByteChannel,SockeChannel 同樣通過它的靜態方法 open() 來創建
public static SocketChannel open() throws IOException
// 帶引數的構造方法還會建立與遠程服務器的連接
public static SocketChannel open(SocketAddress remote) throws IOException
SocketChannel 的主要方法如下:
// 回傳ServerSocketChannel所能產生的事件,這個方法總是回傳以下值
// SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE
public final int validOps()
// 回傳SocketChannel關聯的Socket物件
public Socket socket()
// 建立遠程連接,當處于非阻塞模式時,如果立即連接成功回傳true,否則回傳false
public boolean connect(SocketAddress remote) throws IOException
// 判斷底層Socket是否已經建立遠程連接
public boolean isConnected()
// 判斷是否正在進行遠程連接,如果遠程連接操作已經開始,但還沒有完成,則回傳true,否則回傳false
// 也就是說,無論底層Socket還沒有開始連接,或者已經連接成功,該方法都會回傳false
public boolean isConnectionPending()
// 試圖完成連接遠程服務器的操作
// 非阻塞模式下,建立連接從呼叫connect()方法開始,到呼叫finishConnect()方法結束
// 如果在呼叫此方法之前連接已經建立,則立即回傳true,否則立即回傳false
// 阻塞模式下,如果連接操作還沒有完成,則會進入阻塞狀態,直到連接完成,或者出現IO例外
public boolean finishConnect) throws IOException
// 從Channel讀入若干位元組,存放到引數指定的ByteBuffer
// 假設ByteBuffer剩余容量為r,阻塞模式下,該方法會爭取讀到r位元組
// 如果輸入流中不足r位元組,就進入阻塞狀態,直到讀入了r位元組,或者讀到了輸入流末尾,或者出現了IO例外
// 非阻塞模式下,該方法奉行能讀到多少資料就讀多少資料的原則
// 通道中的可讀資料,有可能不足r位元組,或者為0位元組,總是立即回傳
// 該方法回傳實際上讀入的位元組數,有可能為0,如果回傳-1,表示讀到了輸入流的末尾
public int read(ByteBuffer dst) throws IOException
// 把引數src指定的ByteBuffer的位元組寫到Channel
// 假設ByteBuffer剩余容量為r,阻塞模式下,該方法會爭取輸出r位元組
// 如果底層網路的輸出緩沖區不能容納r位元組,就進入阻塞狀態,直到輸出了r位元組,或者出現了IO例外
// 非阻塞模式下,該方法奉行能輸出多少資料就輸出多少資料的原則,有可能不足r位元組,或者為0位元組,總是立即回傳
// 該方法回傳實際上輸出的位元組,有可能為0
public int write(ByteBuffer src) throws IOException
Selector 類
只要 ServerSockerChannel 以及 SocketChannel 向 Selector 注冊了特定的事件,Selector 就會監控這些事件是否發生,SelectableChannel 的 register() 方法負責注冊事件,該方法回傳 SelectionKey 物件,該物件是用于跟蹤這些被注冊事件的句柄
Selector 物件中會包含三種型別的 SelectionKey 的集合:
all-keys:當前所有向Selector注冊的SelectionKey的集合,Selector的keys()方
法回傳該集合selected-keys:相關事件已經被Selector捕獲的SelectionKey的集合,Selector
的selectedKeys()方法回傳該集合cancelled-keys:已經被取消的SelectionKey的集合,Selector沒有提供訪問這
種集合的方法
當執行 SelectableChannel 的 registe() 方法,會新建一個 SelectionKey 并加入 Selector 的 all-keys 集合中,如果關閉了與 SelectionKey 物件關聯的 Channel 物件,或者呼叫了 SelectionKey 物件的 cancel() 方法,那么這個 SelectionKey 物件就會被加入 cancelled-keys 集合,表示已經被取消,在程式下一次執行 Selector 的 select() 方法時,被取消的 SelectionKey 物件將從所有的集合(包括 all-keys 集合、selected-keys 集合和 cancelled-keys 集合)中被洗掉
在執行 Selector 的 select() 方法時,如果與 SelectionKey 相關的事件發生了,這個 SelectionKey 就被加入 selected-keys 集合中,程式直接呼叫 selected-keys 集合的 remove() 方法,或者呼叫它的 Iterator 的 remove() 方法,都可以從 selected-keys 集合中洗掉一個 SelectionKey 物件
程式不允許直接通過集合介面的 remove() 方法洗掉 all-keys 集合中的 SelectionKey 物件,這會導致 UnsupportedOperationException
Selector 類的主要方法如下:
// Selector的靜態工廠方法,創建一個Selector物件
public static Selector open() throws IOException
// 判斷Selector是否處于打開狀態,Selector物件創建后就處于打開狀態,當呼叫close()方法就進入關閉狀態
public boolean isOpen()
// 回傳Seleclor的all-keys集合,包含了所有與Seclector關聯的SelectionKey物件
public Set<SelectionKey> keys()
// 回傳相關事件已經發生的SelectionKey物件的數目
// 該方法采用非阻塞的作業方式,回傳當前相關事件已經發生的SelectionKey物件的數目,如果沒有,就立即回傳0
public int selectNow() throws IOException
// 回傳相關事件已經發生的SelectionKey物件的數目
// 該方法采用阻塞的作業方式,如果一個也沒有,就進入阻塞狀態,直到出現以下情況之一,就會從select()回傳:
// 1.至少有一個SelectionKey的相關事件已經發生
// 2.其他執行緒呼叫了Selector的wakeup()方法
// 3.當前執行select()方法的執行緒被其他執行緒中斷
// 4.超出了等待時間
public int select() throws IOException
public int select(long timeout) throws IOException
// 喚醒執行Selector的select()方法
public Selector wakeup()
// 關閉 Selector
// 如果有其他執行緒正執行這個Selector的select()方法并且處于阻塞狀態,這個執行緒會立即回傳
// close()方法使得Selector占用的所有資源都被釋敗,所有關聯的SelectionKey都被取消
public void close() throws IOException
SelectionKey 類
SelectionKey 中定義了四種事件,分別用四個 int 型別的常量來表示:
SelectionKey.OP_ACCEPT:接收連接就緒事件,表示服務器監聽到了客戶連接,服務器可以接收這個連接了,常量值為 16SeiectionKey.OP_CONNECT:連接就緒事件表示客戶與服務器的連接已經建立成功,常量值為 8SelectionKey.OP_READ:讀就緒事件,表示通道中已經有了可讀資料,可以執行讀操作了,常量值為 1SelectionKey.OP_WRITE:寫就緒事件表示已經可以向通道寫資料了,常量值為 4
以上常量分別占據不同的二進制位,因此可以通過二進制的或運算來將它們進行任意組合
一個 SelectionKey 物件中包含兩種型別的事件:
-
所有感興趣的事件:通過
SelectableChannel的register()方法注冊事件時,可以在引數中指定SelectionKey感興趣的事件SelectionKey key = socketChannel.register(selector,SelectionKey.OP_CONNECT | SelectionKey.OP_READ);該代碼表示這個
SelectionKey對讀就緒和寫就緒事件感興趣,與之關聯的Selector物件會負責監控這些事件SelectionKey的帶引數的interestOps(int ops)方法也可以為SelectionKey物件增加一個感興趣的事件,如下代碼所示:key.interestOps(SelectionKey.OP_WRITE); -
所有已經發生的事件:
SeletionKey的readyOps()方法回傳所有已經發生的事件,例如假定回傳值為SelectionKey.OP_WRITE | SelectionKey.OP_READ,表示讀就緒和寫就緒事件已經發生了,這意味著與之關聯的SocketChannel物件可以進行讀操作和寫操作了
SelectionKey 的主要方法如下:
// 回傳與這個SelectionKey物件關聯的SelectableChannel物件
public SelectableChannel channel()
// 回傳與這個SelectionKey物件關聯的Selector物件
public Selector selector()
// 判斷這個SelectionKey是否有效
// 當SelectionKey物件創建后,它就一直處于有效狀態
// 如果呼叫了它的cancel()方法,或關閉了與它關聯的SelectableChannel或Selector物件,它就失效
public boolean isValid()
// 使SelectionKey物件失效
public void cancel()
// 回傳這個SelectionKey感興趣的事件
public int interestOps()
// 為SelectionKey增加感興趣的事件
public SelectionKey interestOps(int ops)
// 回傳已經就緒的事件
public int readyOps()
// 判斯與之關聯的SocketChannel的讀就緒事件是否已經發生
public final boolean isReadable()
// 判斷與之關聯的SocketChannel的寫就緒事件是否已經發生
public final boolean isWritable()
// 判斷與之關聯的SocketChannel的連接就緒事件是否已經發生
public final boolean isConnectable()
// 判斷與之關聯的ServerSocketChannel的接收連接就緒事件是否已經發生
public final boolean isAcceptable()
// 使SelectionKey關聯一個附件,一個SelectionKey物件只能關聯一個Object型別的附件
// 如果多次呼叫該方法,則只有最后一個附件與SelectionKey物件關聯
public final Object attach(Object obj)
// 回傳與SelectionKey物件關聯的附件
public final Object attachment()
Channels 類
Channels 類是一個簡單的工具類,提供了通道與傳統的基于 IO 的流、Reader 和 Writer 之間進行轉換的靜態方法
ReadableByteChannel newChannel(InputStream in) // 輸入流轉換成讀通道
WritableByteChannel newChannel(OutputStream out) // 輸出流轉換成寫通道
InputStream newInputStream(AsynchronousByteChannel ch) // 異步通道轉換成輸入流
InputStream newInputStream(ReadableByteChannel ch) // 讀通道轉換成輸入流
OutputStream newOutputStream(AsynchronousByteChannel ch) // 異步通道轉換成輸出流
OutputStream newOutputStream(WritableByteChannel ch) // 寫通道轉換成輸出流
Reader newReader(ReadableByteChannel ch,String csName) // 讀通道轉換成Reader,引數csName指定字符編碼
Reader newReader(ReadableByteChannel ch,Charset charset)//讀通道轉換成Reader.引數charset指定字符編碼
Reader newReader(ReadableByteChannel ch,CharsetDecoder dec, int minBufferCap) // 讀通道轉換成 Reader,引數dec指定字符解碼器,引數minBufferCap指定內部位元組緩沖區的最小容量
Writer newWriter(WritableByeChannel ch, String csName) // 寫通道轉換Writer.引數csName指定字符編碼
Writer newWriter(WritableByeChannel ch, Charset charset) // / 寫通道轉換Writer.引數charset指定字符編碼
Writer newWriter(WritableByeChannel ch, CharsetEncoder enc, int minBufferCap) // 寫通道轉換成Writer,引數dec指定字符解碼器,引數minBufferCap指定內部位元組緩沖區的最小容量
Socket 選項
從 JDK7 開始,SocketChannel、ServerSocketChannel、AsynchronousSocketChannel、AsynchronousServerSocketChannel 和 DatagramChannel 都實作了新的 NetworkChannel 介面,NetworkChannel 介面的主要作用是設定和讀取各種 Socket 選項
NetworkChannel 介面提供了用于設定和讀取這些選項的方法:
<T> T getOption(SocketOption<T> name) // 獲取特定的Socket選項值
<T> NetworkChannel setOption(SocketOption<T> name, T value) // 設定特定的Socket選項
Set<SocketOption<?>> supportedOptions() // 獲取所有支持的Socket選項
SocketOptionl 類是一個泛型類,SocketOption<T> 中的 T 代表特定選項的取值型別,可選值包括 Integer、Boolean 和 NetworkInterface
StandardSocketOptions 類提供了以下表示特定選項的常量:
SocketOption<NetworkInterface> -- StandardSocketOptions.IP_MULTICAST_IF
SocketOption<Boolean> -- StandardSocketOptions.IP_MULTICAST_LOOP
SocketOption<Integer> -- StandardSocketOptions.IP_MULTICAST_TTL
SocketOption<Integer> -- StandardSocketOptions.IP_TOS
SocketOption<Boolean> -- StandardSocketOptions.SO_BROADCAST
SocketOption<Boolean> -- StandardSocketOptions.SO_KEEPALIVE
SocketOption<Integer> -- StandardSocketOptions.SO_LINGER
SocketOption<Integer> -- StandardSocketOptions.SO_RCVBUF
SocketOption<Boolean> -- StandardSocketOptions.SO_REUSEADDR
SocketOption<Boolean> -- StandardSocketOptions.SO_REUSEPORT
SocketOption<Integer> -- StandardSocketOptions.SO_SNDBUF
SocketOption<Boolean> -- StandardSocketOptions.TCP_NODELAY
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/552418.html
標籤:其他
上一篇:Java的執行緒
下一篇:返回列表
