NIO(Non-blocking I/O,在 Java 領域,也稱為 New I/O),是一種同步非阻塞的I/O模型,也是I/O多路複用的基礎。那和普通 IO 有什麼區別呢?
一、概述
NIO 是從 Java 1.4 版本開始引入的一個新的 IO API,NIO 支持面向緩衝區的、基於通道的 IO 操作。
原來的 IO 是阻塞式 IO,與 NIO 的對比:
| IO | NIO |
|---|---|
| 面向流 | 面向緩衝 |
| 阻塞 IO | 非阻塞 IO |
| 無 | 選擇器 |
1、面向流和麪向緩衝
面向流是每次從流中讀取一個或多個字節,直到讀取完。讀過了就過了。
面向緩衝是把數據先放到緩衝區中,需要讀取的時候從緩衝區拿,緩衝區數據讀取後還在,還能通過指針移動讀取緩衝區中不同的數據。
2、阻塞/非阻塞
阻塞就是不讀寫數據的時候也要佔着線程,不能幹別的。非阻塞是不讀寫的時候該線程可以幹別的事情。
3、選擇器
允許一個線程管理多個輸入通道。
使用場景:
NIO:需要管理同時打開的多個連接(如聊天服務器),連接每次僅發送少量數據。
IO:少量連接,每次發送大量數據
處理數據的流程:
阻塞 IO
NIO
二、NIO 基礎
Buffer和 Channel是標準NIO中的核心對象。
- Channel 是對原IO中流的模擬,任何來源和目的數據都必須通過一個Channel對象
- Buffer 實質上是一個容器對象,發給 Channel 的所有對象都必須先放到 Buffer 中;同樣的,從 Channel 中讀取的任何數據都要讀到 Buffer 中。
一)關於 Buffer
Buffer 包含一些要寫入或讀出的數據。在 NIO 中,所有的數據都是用 Buffer 處理的,它是 NIO 讀寫數據的中轉池。Buffer 實質上是一個數組,提供對數據結構化訪問的功能。
使用 Buffer 讀寫數據一般有以下四個步驟:
- 寫入數據到 Buffer;
- 調用 flip() 方法,從寫模式轉換為讀模式;
- 從 Buffer 中讀取數據;
- 調用 clear() 方法(清空);或者 compact() 方法(清除已讀數據)。
二)關於 Channel
可以通過 Channel 讀取和寫入數據。可以把它看做 IO 中的流。但是它和流相比還有一些不同。
- Channel 是雙向的,既可以讀又可以寫,而流是單向的
- Channel 可以進行異步的讀寫
- 對 Channel 的讀寫必須通過 buffer 對象
三、案例 demo
下面通過一個案例講解 NIO 的操作過程。
將從一個文件中讀取數據,寫入到另一個文件中,代碼如下
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class CopyFileUseNIO {
public static void main(String[] args) throws IOException {
String src = "/xxxx/LICENSE.txt";
String dst = "/xxxx/LICENSE-COPY.txt";
FileInputStream fi = new FileInputStream(src);
FileOutputStream fo = new FileOutputStream(dst);
//獲得傳輸通道channel
FileChannel inChannel = fi.getChannel();
FileChannel outChannel = fo.getChannel();
//獲得容器buffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (true) {
//判斷是否讀完文件
int eof = inChannel.read(buffer); // #1
if (eof == -1) {
break;
}
//重設一下buffer的position=0,limit=position
buffer.flip(); // #2
//開始寫
outChannel.write(buffer); // #3
//寫完要重置buffer,重設position=0,limit=capacity
buffer.clear(); // #4
}
inChannel.close();
outChannel.close();
fi.close();
fo.close();
}
}
關注上面有註釋的 #1 ~ #4 四個位置。
四個位置代碼執行後 buffer 數據如下所示,參考下面的可以幫你更好的理解,幾個操作都做了什麼事情。
當然,也可以直接看源碼,很簡潔易懂:(如 clear 的操作邏輯)
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
四、總結
本節對 NIO 進行了簡要介紹。與原 IO 進行了對比,對 NIO 的讀寫流程進行了介紹,講解了 NIO 重要的概念 buffer 和 channel。所有的數據必須經過 buffer 處理,channel 僅是連接的管道,不能直接從 channel 中獲取數據。
NIO 有非阻塞的特性,不會在讀寫未完畢時持續阻塞線程,當未在讀寫時,線程可以處理別的事情。
最後提供了讀寫數據的 demo 幫助理解。
參考文章:
Java NIO淺析
Java常見面試題彙總-----------Java基礎(NIO與IO的區別)
Java NIO 詳解(一)