一、前言
筆者之前接觸的NIO程式設計比較少,所以對這一塊的基礎也比較弱,NIO作為java程式設計中一個重要的模組,不能很好的掌握它,感覺自己在java方面就掌握的不夠,所以,接下來,筆者會學習NIO程式設計,所以,該系列文章不會涉及到很深原始碼解析,純粹的是學習課程,也可以理解為筆者的筆記,記錄學習NIO的過程,同時也希望這類文章可以對同樣想掌握NIO程式設計的你有幫助。
二、什麼是NIO?
Java NIO(New IO)是一個可以替代標準Java IO API的IO API(從Java 1.4開始),Java NIO提供了與標準IO不同的IO工作方式。NIO可以理解為非阻塞IO,傳統的IO的read和write只能阻塞執行,執行緒在讀寫IO期間不能幹其他事情,比如呼叫socket.read()時,如果伺服器一直沒有資料傳輸過來,執行緒就一直阻塞,而NIO中可以配置socket為非阻塞模式。
三、IO和NIO的區別
- IO是面向位元組流和字元流的,而NIO是面向緩衝區的。
- IO是阻塞模式的,NIO是非阻塞模式的
- NIO新增了選擇器的概念,可以通過選擇器監聽多個通道。
四、NIO相關概念
Channel(通道)
Java NIO的通道類似流,但又有些不同:
既可以從通道中讀取資料,又可以寫資料到通道。但流的讀寫通常是單向的。通道可以非同步地讀寫。
通道中的資料總是要先讀到一個Buffer,或者總是要從一個Buffer中寫入。
Channel的實現
這些是Java NIO中最重要的通道的實現:
-
FileChannel: 從檔案中讀寫資料。
-
DatagramChannel : 能通過UDP讀寫網路中的資料。
-
SocketChannel: 能通過TCP讀寫網路中的資料。
-
ServerSocketChannel :可以監聽新進來的TCP連線,像Web伺服器那樣。對每一個新進來的連線都會建立一個SocketChannel。
Buffer(緩衝區)
緩衝區本質上是一塊可以寫入資料,然後可以從中讀取資料的記憶體。這塊記憶體被包裝成NIO Buffer物件,並提供了一組方法,用來方便的訪問該塊記憶體。
Buffer(緩衝區)的主要屬性
屬性 | 功能 |
---|---|
capacity | 容量 |
position | 緩衝區當前位置指標,最大可為capacity – 1 |
limit | 緩衝區最大讀取和寫入限制 |
buffer屬性示意圖:
上圖展示了寫模式和讀模式下,以上屬性的示意圖,寫模式下,limit和capacity是一樣的,這表示你能寫入的最大容量資料,讀模式下,limit會和position一樣,表示你能讀到寫入的全部資料。
Buffer(緩衝區)的主要分類
- ByteBuffer
- MappedByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
其實以上基本上為了接收不同的資料型別而對應的,只有一個特殊的MappedByteBuffer,本次先不學習它,後續再去了解它。
五、實戰
我們主要以理解上面介紹的概念為目的實現一個簡單的NIO程式設計,讀取資料夾內的檔案,然後輸出到控制檯。
public static void testNio(){
try {
RandomAccessFile rdf=new RandomAccessFile("E:\\nio\\niotest.txt","rw");
//利用channel中的FileChannel來實現檔案的讀取 FileChannel inChannel= rdf.getChannel();
//設定緩衝區容量為10 ByteBuffer buf= ByteBuffer.allocate(10);
//從通道中讀取資料到緩衝區,返回讀取的位元組數量 int byteRead=inChannel.read(buf);
//數量為-1表示讀取完畢。 while (byteRead!=-1){
//切換模式為讀模式,其實就是把postion位置設定為0,可以從0開始讀取 buf.flip();
//如果緩衝區還有資料 while (buf.hasRemaining()){
//輸出一個字元 System.out.print((char) buf.get());
} //資料讀完後清空緩衝區 buf.clear();
//繼續把通道內剩餘資料寫入緩衝區 byteRead = inChannel.read(buf);
} //關閉通道 rdf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}複製程式碼
我在磁碟中niotest.txt的內容是hello world,我們看看執行結果是不是hello world。
結果和預期一樣,我們完成了NIO程式設計輸出磁碟檔案內容,其實上面程式碼中,我第一印象有疑問的地方就是buf.flip()
,其他只要用過IO的應該都能理解,既然不能理解我們就先除錯下,註釋掉該段內容,看看輸出結果如何。
註釋掉buf.flip()
後,輸出結果果真不對了,那究竟是為啥呢?
上圖是第一個迴圈,我們可以看到緩衝區的position=10,limit=10,cap=10。呼叫buf.hasRemaining()
為false,所以buffer第一次沒有輸出任何東西。看看第二次迴圈
其實這整個迴圈可以解析步驟如下:
-
buffer讀取了10位元組內容,內容就是:hello worl。
-
buf.hasRemaining() 判斷為false,直接清空buffer(直接把position復位為0,可以直接覆蓋內容),讀取剩下內容。
-
最後只剩下一個字元
d
讀取,這個時候讀取完後,buffer內容如下:dello worl。 -
最後輸出因為從postion=1位置輸出,所以輸出:ello worl。
根據以上解析,我們發現註釋了buf.flip()後,position位置寫入是什麼位置,讀出就是什麼位置,所以,該方法應該就是把position賦值為0,從開始讀取。解讀原始碼也證實了我的猜想,原始碼如下:
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}複製程式碼
參考
《JAVA NIO》