柚子快報邀請碼778899分享:網(wǎng)絡(luò) io,nio,aio區(qū)別
柚子快報邀請碼778899分享:網(wǎng)絡(luò) io,nio,aio區(qū)別
文章目錄
前言io類型介紹同步阻塞io同步非阻塞ioio多路復(fù)用異步io
普通ioaionioChannelChannel實現(xiàn)基本的 Channel代碼 示例
BufferBuffer的基本用法Buffer的capacity,position和limitcapacitypositionlimit
Buffer的類型Buffer的分配向Buffer中寫數(shù)據(jù)從Buffer中讀取數(shù)據(jù)
Selector為什么使用Selector?Selector的創(chuàng)建向Selector注冊通道SelectionKey通過Selector選擇通道
總結(jié)
前言
說起io,大家一定十分的熟悉。因為不管是什么編程語言,都離不開io操作。不管是對本地文件的io操作,還是網(wǎng)絡(luò)的io流,在日常的程序開發(fā)中都是十分的常見。那么今天我們就詳細(xì)的介紹一下io,nio,aio的區(qū)別,讓大家對io操作有個比較深刻的理解。 UNIX 系統(tǒng)下的 I/O 模型有 5 種:同步阻塞 I/O、同步非阻塞 I/O、I/O 多路復(fù)用、信號驅(qū)動 I/O 和異步 I/O。而所謂的io,nio,aio只是他們的設(shè)計和使用的io模型不同。
io類型介紹
這里我們主要介紹java中使用到的io模型 首先我們介紹io模型之前,大家要清楚一個io操作大致流程是怎樣的。這里我們拿網(wǎng)絡(luò)io進(jìn)行舉例。當(dāng)一個網(wǎng)絡(luò)io開始的時候,將會設(shè)計到兩個對象,一個是調(diào)用io的用戶線程,還有一個是讀取數(shù)據(jù)的操作系統(tǒng)內(nèi)核,一個進(jìn)程的地址地址空間分為用戶空間和內(nèi)核空間。用戶線程不能直接訪問內(nèi)核空間。 當(dāng)用戶發(fā)起io操作的時候,會經(jīng)歷兩個步驟 1.用戶線程等待內(nèi)核從網(wǎng)卡讀取數(shù)據(jù)到內(nèi)核空間 2.內(nèi)核將數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間 各個io模型的不同就是實現(xiàn)這個兩步驟方式不一樣
同步阻塞io
同步阻塞 I/O:用戶線程發(fā)起 read 調(diào)用后就阻塞了,讓出 CPU。內(nèi)核等待網(wǎng)卡數(shù)據(jù)到來,把數(shù)據(jù)從網(wǎng)卡拷貝到內(nèi)核空間,接著把數(shù)據(jù)拷貝到用戶空間,再把用戶線程叫醒。
同步非阻塞io
同步阻塞型io:用戶線程不斷的發(fā)起調(diào)用read,在數(shù)據(jù)讀取到內(nèi)核空間之前,read一直返回失敗,直到數(shù)據(jù)進(jìn)入內(nèi)核空間,才返回成功,但是在等內(nèi)核將數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間的過程仍是阻塞。直到數(shù)據(jù)到了用戶空間,該線程才會從阻塞狀態(tài)喚醒。
io多路復(fù)用
io多路復(fù)用:io多路復(fù)用將讀取操作分為了兩個步驟,首先是讓select詢問數(shù)據(jù)是否準(zhǔn)備好了,數(shù)據(jù)準(zhǔn)備好了,也就是數(shù)據(jù)到了內(nèi)核空間,才開始發(fā)起read調(diào)用。在等待數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間這段時間里,線程還是阻塞的。那為什么叫 I/O 多路復(fù)用呢?因為一次 select 調(diào)用可以向內(nèi)核查多個數(shù)據(jù)通道(Channel)的狀態(tài),所以叫多路復(fù)用。
異步io
異步io:用戶再發(fā)起read調(diào)用的時候,同時注冊一個回調(diào)函數(shù),然后read立即返回,等到數(shù)據(jù)到達(dá)用戶空間之后,直接執(zhí)行回調(diào)函數(shù)即可。整個過程沒有一點阻塞。
普通io
普通io就是大家日常使用的java io,例如 inputstream等,都是普通的io,使用的時候,就會阻塞整個線程,直到數(shù)據(jù)拷貝到用戶空間為止。
aio
服務(wù)器端
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class AIOEchoServer {
public final static int PORT = 8001;
public final static String IP = "127.0.0.1";
private AsynchronousServerSocketChannel server = null;
public AIOEchoServer(){
try {
//同樣是利用工廠方法產(chǎn)生一個通道,異步通道 AsynchronousServerSocketChannel
server = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(IP,PORT));
} catch (IOException e) {
e.printStackTrace();
}
}
//使用這個通道(server)來進(jìn)行客戶端的接收和處理
public void start(){
System.out.println("Server listen on "+PORT);
//注冊事件和事件完成后的處理器,這個CompletionHandler就是事件完成后的處理器
server.accept(null,new CompletionHandler
final ByteBuffer buffer = ByteBuffer.allocate(1024);
@Override
public void completed(AsynchronousSocketChannel result,Object attachment) {
System.out.println(Thread.currentThread().getName());
Future
try{
buffer.clear();
result.read(buffer).get(100,TimeUnit.SECONDS);
System.out.println("In server: "+ new String(buffer.array()));
//將數(shù)據(jù)寫回客戶端
buffer.flip();
writeResult = result.write(buffer);
}catch(InterruptedException | ExecutionException | TimeoutException e){
e.printStackTrace();
}finally{
server.accept(null,this);
try {
writeResult.get();
result.close();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("failed:"+exc);
}
});
}
public static void main(String[] args) {
new AIOEchoServer().start();
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
public class AIOClient {
public static void main(String[] args) throws IOException {
final AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
InetSocketAddress serverAddress = new InetSocketAddress("127.0.0.1",8001);
CompletionHandler
@Override
public void completed(Void result, Object attachment) {
client.write(ByteBuffer.wrap("Hello".getBytes()),null,
new CompletionHandler
@Override
public void completed(Integer result,
Object attachment) {
final ByteBuffer buffer = ByteBuffer.allocate(1024);
client.read(buffer,buffer,new CompletionHandler
@Override
public void completed(Integer result,
ByteBuffer attachment) {
buffer.flip();
System.out.println(new String(buffer.array()));
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc,
ByteBuffer attachment) {
}
});
}
@Override
public void failed(Throwable exc, Object attachment) {
}
});
}
@Override
public void failed(Throwable exc, Object attachment) {
}
};
client.connect(serverAddress, null, handler);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
上面是一個簡單的aio例子,上面的代碼我們可以看出,相較于普通的io,aio多了一個回調(diào)事件,這個也是aio實現(xiàn)異步的關(guān)鍵,這個回調(diào)事件里面正是處理數(shù)據(jù)的方法。因此,aio可以不用等數(shù)據(jù)到達(dá)再執(zhí)行處理時間,而是變成數(shù)據(jù)到達(dá)后調(diào)用回調(diào)事件,自己處理自己。
nio
nio使用的應(yīng)該是io多路復(fù)用模型,其中比較重要的組件有三個,Selectors,Channels,Buffers。而nio的工作機(jī)制主要就是,selector不斷輪詢,查詢多個通道,檢測通道數(shù)據(jù)是否到達(dá),如果到達(dá)。則將該通道標(biāo)志為就緒狀態(tài),然后可以將通道數(shù)據(jù)讀取到buffer里面,然后再進(jìn)行后續(xù)操作。接下來,我們將分別介紹這三個組件
Channel
Channel實現(xiàn)
這些是Java NIO中最重要的通道的實現(xiàn):
FileChannel DatagramChannel SocketChannel ServerSocketChannel FileChannel 從文件中讀寫數(shù)據(jù)。
DatagramChannel 能通過UDP讀寫網(wǎng)絡(luò)中的數(shù)據(jù)。
SocketChannel 能通過TCP讀寫網(wǎng)絡(luò)中的數(shù)據(jù)。
ServerSocketChannel可以監(jiān)聽新進(jìn)來的TCP連接,像Web服務(wù)器那樣。對每一個新進(jìn)來的連接都會創(chuàng)建一個SocketChannel。
基本的 Channel代碼 示例
下面是一個使用FileChannel讀取數(shù)據(jù)到Buffer中的示例:
[code lang=”java”]
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel inChannel = aFile.getChannel();
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buf);
while (bytesRead != -1) {
System.out.println("Read " + bytesRead);
buf.flip();
while(buf.hasRemaining()){
System.out.print((char) buf.get());
}
buf.clear();
bytesRead = inChannel.read(buf);
}
aFile.close();
[/code]
Buffer
Buffer的基本用法
使用Buffer讀寫數(shù)據(jù)一般遵循以下四個步驟:
寫入數(shù)據(jù)到Buffer 調(diào)用flip()方法 從Buffer中讀取數(shù)據(jù) 調(diào)用clear()方法或者compact()方法 當(dāng)向buffer寫入數(shù)據(jù)時,buffer會記錄下寫了多少數(shù)據(jù)。一旦要讀取數(shù)據(jù),需要通過flip()方法將Buffer從寫模式切換到讀模式。在讀模式下,可以讀取之前寫入到buffer的所有數(shù)據(jù)。
一旦讀完了所有的數(shù)據(jù),就需要清空緩沖區(qū),讓它可以再次被寫入。有兩種方式能清空緩沖區(qū):調(diào)用clear()或compact()方法。clear()方法會清空整個緩沖區(qū)。compact()方法只會清除已經(jīng)讀過的數(shù)據(jù)。任何未讀的數(shù)據(jù)都被移到緩沖區(qū)的起始處,新寫入的數(shù)據(jù)將放到緩沖區(qū)未讀數(shù)據(jù)的后面。
下面是一個使用Buffer的例子:
[code lang=”java”]
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel inChannel = aFile.getChannel();
//create buffer with capacity of 48 bytes
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buf); //read into buffer.
while (bytesRead != -1) {
buf.flip(); //make buffer ready for read
while(buf.hasRemaining()){
System.out.print((char) buf.get()); // read 1 byte at a time
}
buf.clear(); //make buffer ready for writing
bytesRead = inChannel.read(buf);
}
aFile.close();
[/code]
Buffer的capacity,position和limit
緩沖區(qū)本質(zhì)上是一塊可以寫入數(shù)據(jù),然后可以從中讀取數(shù)據(jù)的內(nèi)存。這塊內(nèi)存被包裝成NIO Buffer對象,并提供了一組方法,用來方便的訪問該塊內(nèi)存。
為了理解Buffer的工作原理,需要熟悉它的三個屬性:
capacity position limit position和limit的含義取決于Buffer處在讀模式還是寫模式。不管Buffer處在什么模式,capacity的含義總是一樣的。
這里有一個關(guān)于capacity,position和limit在讀寫模式中的說明,詳細(xì)的解釋在插圖后面。
capacity
作為一個內(nèi)存塊,Buffer有一個固定的大小值,也叫“capacity”.你只能往里寫capacity個byte、long,char等類型。一旦Buffer滿了,需要將其清空(通過讀數(shù)據(jù)或者清除數(shù)據(jù))才能繼續(xù)寫數(shù)據(jù)往里寫數(shù)據(jù)。
position
當(dāng)你寫數(shù)據(jù)到Buffer中時,position表示當(dāng)前的位置。初始的position值為0.當(dāng)一個byte、long等數(shù)據(jù)寫到Buffer后, position會向前移動到下一個可插入數(shù)據(jù)的Buffer單元。position最大可為capacity – 1.
當(dāng)讀取數(shù)據(jù)時,也是從某個特定位置讀。當(dāng)將Buffer從寫模式切換到讀模式,position會被重置為0. 當(dāng)從Buffer的position處讀取數(shù)據(jù)時,position向前移動到下一個可讀的位置。
limit
在寫模式下,Buffer的limit表示你最多能往Buffer里寫多少數(shù)據(jù)。 寫模式下,limit等于Buffer的capacity。
當(dāng)切換Buffer到讀模式時, limit表示你最多能讀到多少數(shù)據(jù)。因此,當(dāng)切換Buffer到讀模式時,limit會被設(shè)置成寫模式下的position值。換句話說,你能讀到之前寫入的所有數(shù)據(jù)(limit被設(shè)置成已寫數(shù)據(jù)的數(shù)量,這個值在寫模式下就是position)
Buffer的類型
Java NIO 有以下Buffer類型
ByteBuffer MappedByteBuffer CharBuffer DoubleBuffer FloatBuffer IntBuffer LongBuffer ShortBuffer p<> 如你所見,這些Buffer類型代表了不同的數(shù)據(jù)類型。換句話說,就是可以通過char,short,int,long,float 或 double類型來操作緩沖區(qū)中的字節(jié)。
MappedByteBuffer 有些特別,在涉及它的專門章節(jié)中再講。
Buffer的分配
要想獲得一個Buffer對象首先要進(jìn)行分配。 每一個Buffer類都有一個allocate方法。下面是一個分配48字節(jié)capacity的ByteBuffer的例子。
[code lang=”java”]
ByteBuffer buf = ByteBuffer.allocate(48);
[/code]
這是分配一個可存儲1024個字符的CharBuffer:
[code lang=”java”]
CharBuffer buf = CharBuffer.allocate(1024);
[/code]
向Buffer中寫數(shù)據(jù)
寫數(shù)據(jù)到Buffer有兩種方式:
從Channel寫到Buffer。 通過Buffer的put()方法寫到Buffer里。 從Channel寫到Buffer的例子
[code lang=”java”]
int bytesRead = inChannel.read(buf); //read into buffer.
[/code]
通過put方法寫B(tài)uffer的例子:
[code lang=”java”]
buf.put(127);
[/code]
put方法有很多版本,允許你以不同的方式把數(shù)據(jù)寫入到Buffer中。例如, 寫到一個指定的位置,或者把一個字節(jié)數(shù)組寫入到Buffer。 更多Buffer實現(xiàn)的細(xì)節(jié)參考JavaDoc。
flip()方法 flip方法將Buffer從寫模式切換到讀模式。調(diào)用flip()方法會將position設(shè)回0,并將limit設(shè)置成之前position的值。
換句話說,position現(xiàn)在用于標(biāo)記讀的位置,limit表示之前寫進(jìn)了多少個byte、char等 —— 現(xiàn)在能讀取多少個byte、char等。
從Buffer中讀取數(shù)據(jù)
從Buffer中讀取數(shù)據(jù)有兩種方式:
1.從Buffer讀取數(shù)據(jù)到Channel。 2.使用get()方法從Buffer中讀取數(shù)據(jù)。 從Buffer讀取數(shù)據(jù)到Channel的例子:
[code lang=”java”]
//read from buffer into channel.
int bytesWritten = inChannel.write(buf);
[/code]
使用get()方法從Buffer中讀取數(shù)據(jù)的例子
[code lang=”java”]
byte aByte = buf.get();
[/code]
get方法有很多版本,允許你以不同的方式從Buffer中讀取數(shù)據(jù)。例如,從指定position讀取,或者從Buffer中讀取數(shù)據(jù)到字節(jié)數(shù)組。更多Buffer實現(xiàn)的細(xì)節(jié)參考JavaDoc。
Selector
為什么使用Selector?
僅用單個線程來處理多個Channels的好處是,只需要更少的線程來處理通道。事實上,可以只用一個線程處理所有的通道。對于操作系統(tǒng)來說,線程之間上下文切換的開銷很大,而且每個線程都要占用系統(tǒng)的一些資源(如內(nèi)存)。因此,使用的線程越少越好。
但是,需要記住,現(xiàn)代的操作系統(tǒng)和CPU在多任務(wù)方面表現(xiàn)的越來越好,所以多線程的開銷隨著時間的推移,變得越來越小了。實際上,如果一個CPU有多個內(nèi)核,不使用多任務(wù)可能是在浪費CPU能力。不管怎么說,關(guān)于那種設(shè)計的討論應(yīng)該放在另一篇不同的文章中。在這里,只要知道使用Selector能夠處理多個通道就足夠了。
Selector的創(chuàng)建
通過調(diào)用Selector.open()方法創(chuàng)建一個Selector,如下:
[code lang=”java”] Selector selector = Selector.open(); [/code]
向Selector注冊通道
為了將Channel和Selector配合使用,必須將channel注冊到selector上。通過SelectableChannel.register()方法來實現(xiàn),如下:
[code lang=”java”]
channel.configureBlocking(false);
SelectionKey key = channel.register(selector,
Selectionkey.OP_READ);
[/code]
與Selector一起使用時,Channel必須處于非阻塞模式下。這意味著不能將FileChannel與Selector一起使用,因為FileChannel不能切換到非阻塞模式。而套接字通道都可以。
注意register()方法的第二個參數(shù)。這是一個“interest集合”,意思是在通過Selector監(jiān)聽Channel時對什么事件感興趣。可以監(jiān)聽四種不同類型的事件:
1.Connect 2.Accept 3.Read 4.Write 通道觸發(fā)了一個事件意思是該事件已經(jīng)就緒。所以,某個channel成功連接到另一個服務(wù)器稱為“連接就緒”。一個server socket channel準(zhǔn)備好接收新進(jìn)入的連接稱為“接收就緒”。一個有數(shù)據(jù)可讀的通道可以說是“讀就緒”。等待寫數(shù)據(jù)的通道可以說是“寫就緒”。
這四種事件用SelectionKey的四個常量來表示:
1.SelectionKey.OP_CONNECT 2.SelectionKey.OP_ACCEPT 3.SelectionKey.OP_READ 4.SelectionKey.OP_WRITE 如果你對不止一種事件感興趣,那么可以用“位或”操作符將常量連接起來,如下:
[code lang=”java”] int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE; [/code]
在下面還會繼續(xù)提到interest集合。
SelectionKey
在上一小節(jié)中,當(dāng)向Selector注冊Channel時,register()方法會返回一個SelectionKey對象。這個對象包含了一些你感興趣的屬性:
interest集合 ready集合 Channel Selector 附加的對象(可選) 下面我會描述這些屬性。
interest集合 就像向Selector注冊通道一節(jié)中所描述的,interest集合是你所選擇的感興趣的事件集合??梢酝ㄟ^SelectionKey讀寫interest集合,像這樣:
[code lang=”java”] int interestSet = selectionKey.interestOps();
boolean isInterestedInAccept = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT; boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT; boolean isInterestedInRead = interestSet & SelectionKey.OP_READ; boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE; [/code]
可以看到,用“位與”操作interest 集合和給定的SelectionKey常量,可以確定某個確定的事件是否在interest 集合中。
ready集合 ready 集合是通道已經(jīng)準(zhǔn)備就緒的操作的集合。在一次選擇(Selection)之后,你會首先訪問這個ready set。Selection將在下一小節(jié)進(jìn)行解釋??梢赃@樣訪問ready集合:
[code lang=”java”] int readySet = selectionKey.readyOps(); [/code]
可以用像檢測interest集合那樣的方法,來檢測channel中什么事件或操作已經(jīng)就緒。但是,也可以使用以下四個方法,它們都會返回一個布爾類型:
[code lang=”java”] selectionKey.isAcceptable(); selectionKey.isConnectable(); selectionKey.isReadable(); selectionKey.isWritable(); [/code]
Channel + Selector 從SelectionKey訪問Channel和Selector很簡單。如下:
[code lang=”java”] Channel channel = selectionKey.channel(); Selector selector = selectionKey.selector(); [/code]
附加的對象 可以將一個對象或者更多信息附著到SelectionKey上,這樣就能方便的識別某個給定的通道。例如,可以附加 與通道一起使用的Buffer,或是包含聚集數(shù)據(jù)的某個對象。使用方法如下:
[code lang=”java”] selectionKey.attach(theObject); Object attachedObj = selectionKey.attachment(); [/code]
還可以在用register()方法向Selector注冊Channel的時候附加對象。如:
[code lang=”java”] SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject); [/code]
通過Selector選擇通道
一旦向Selector注冊了一或多個通道,就可以調(diào)用幾個重載的select()方法。這些方法返回你所感興趣的事件(如連接、接受、讀或?qū)懀┮呀?jīng)準(zhǔn)備就緒的那些通道。換句話說,如果你對“讀就緒”的通道感興趣,select()方法會返回讀事件已經(jīng)就緒的那些通道。
下面是select()方法:
int select() int select(long timeout) int selectNow() select()阻塞到至少有一個通道在你注冊的事件上就緒了。
select(long timeout)和select()一樣,除了最長會阻塞timeout毫秒(參數(shù))。
selectNow()不會阻塞,不管什么通道就緒都立刻返回(譯者注:此方法執(zhí)行非阻塞的選擇操作。如果自從前一次選擇操作后,沒有通道變成可選擇的,則此方法直接返回零。)。
select()方法返回的int值表示有多少通道已經(jīng)就緒。亦即,自上次調(diào)用select()方法后有多少通道變成就緒狀態(tài)。如果調(diào)用select()方法,因為有一個通道變成就緒狀態(tài),返回了1,若再次調(diào)用select()方法,如果另一個通道就緒了,它會再次返回1。如果對第一個就緒的channel沒有做任何操作,現(xiàn)在就有兩個就緒的通道,但在每次select()方法調(diào)用之間,只有一個通道就緒了。
selectedKeys() 一旦調(diào)用了select()方法,并且返回值表明有一個或更多個通道就緒了,然后可以通過調(diào)用selector的selectedKeys()方法,訪問“已選擇鍵集(selected key set)”中的就緒通道。如下所示:
[code lang=”java”] Set selectedKeys = selector.selectedKeys(); [/code]
當(dāng)像Selector注冊Channel時,Channel.register()方法會返回一個SelectionKey 對象。這個對象代表了注冊到該Selector的通道。可以通過SelectionKey的selectedKeySet()方法訪問這些對象。
可以遍歷這個已選擇的鍵集合來訪問就緒的通道。如下:
[code lang=”java”]
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();
}
[/code]
這個循環(huán)遍歷已選擇鍵集中的每個鍵,并檢測各個鍵所對應(yīng)的通道的就緒事件。
注意每次迭代末尾的keyIterator.remove()調(diào)用。Selector不會自己從已選擇鍵集中移除SelectionKey實例。必須在處理完通道時自己移除。下次該通道變成就緒時,Selector會再次將其放入已選擇鍵集中。
SelectionKey.channel()方法返回的通道需要轉(zhuǎn)型成你要處理的類型,如ServerSocketChannel或SocketChannel等。
總結(jié)
我們首先介紹了io的五種模型,然后在此基礎(chǔ)上又介紹了io,nio,aio的區(qū)別和其所使用的模型。但是這個有一點,我們介紹的是nio1.0的版本,2.0版本可能不太一樣,有需要的可以自行前往官網(wǎng)了解學(xué)習(xí)。最后希望通過本篇文章,大家可以對不同的io有一個清晰的認(rèn)知。
柚子快報邀請碼778899分享:網(wǎng)絡(luò) io,nio,aio區(qū)別
參考閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。