博客 / 詳情

返回

【Java基礎】NIO 初步瞭解

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、選擇器
允許一個線程管理多個輸入通道。
image.png

使用場景:
NIO:需要管理同時打開的多個連接(如聊天服務器),連接每次僅發送少量數據。
IO:少量連接,每次發送大量數據

處理數據的流程:
阻塞 IO
image.png

NIO
image.png

二、NIO 基礎

BufferChannel是標準NIO中的核心對象。

  • Channel 是對原IO中流的模擬,任何來源和目的數據都必須通過一個Channel對象
  • Buffer 實質上是一個容器對象,發給 Channel 的所有對象都必須先放到 Buffer 中;同樣的,從 Channel 中讀取的任何數據都要讀到 Buffer 中。

一)關於 Buffer

Buffer 包含一些要寫入或讀出的數據。在 NIO 中,所有的數據都是用 Buffer 處理的,它是 NIO 讀寫數據的中轉池。Buffer 實質上是一個數組,提供對數據結構化訪問的功能。
使用 Buffer 讀寫數據一般有以下四個步驟:

  1. 寫入數據到 Buffer;
  2. 調用 flip() 方法,從寫模式轉換為讀模式;
  3. 從 Buffer 中讀取數據;
  4. 調用 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 數據如下所示,參考下面的可以幫你更好的理解,幾個操作都做了什麼事情。
image.png

當然,也可以直接看源碼,很簡潔易懂:(如 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 詳解(一)

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.