JAVA IO 包
JAVA NIO
NIO 主要有三大核心部分:Channel(通道),Buffer(緩沖區), Selector。
傳統 IO 基于字節流和字 符流進行操作,而 NIO 基于 Channel 和 Buffer(緩沖區)進行操作,數據總是從通道讀取到緩沖區 中,或者從緩沖區寫入到通道中。Selector(選擇區)用于監聽多個通道的事件(比如:連接打開, 數據到達)。因此,單個線程可以監聽多個數據通道。
NIO 和傳統 IO 之間第一個最大的區別是,IO 是面向流的,NIO 是面向緩沖區的。
NIO 的緩沖區
Java IO 面向流意味著每次從流中讀一個或多個字節,直至讀取所有字節,它們沒有被緩存在任何 地方。此外,它不能前后移動流中的數據。如果需要前后移動從流中讀取的數據,需要先將它緩 存到一個緩沖區。NIO 的緩沖導向方法不同。數據讀取到一個它稍后處理的緩沖區,需要時可在 緩沖區中前后移動。這就增加了處理過程中的靈活性。但是,還需要檢查是否該緩沖區中包含所 有您需要處理的數據。而且,需確保當更多的數據讀入緩沖區時,不要覆蓋緩沖區里尚未處理的數據。
NIO 的非阻塞
IO 的各種流是阻塞的。這意味著,當一個線程調用 read() 或 write()時,該線程被阻塞,直到有 一些數據被讀取,或數據完全寫入。該線程在此期間不能再干任何事情了。NIO 的非阻塞模式, 使一個線程從某通道發送請求讀取數據,但是它僅能得到目前可用的數據,如果目前沒有數據可 用時,就什么都不會獲取。而不是保持線程阻塞,所以直至數據變的可以讀取之前,該線程可以 繼續做其他的事情。
非阻塞寫也是如此。一個線程請求寫入一些數據到某通道,但不需要等待它 完全寫入,這個線程同時可以去做別的事情。線程通常將非阻塞 IO 的空閑時間用于在其它通道上 執行 IO 操作,所以一個單獨的線程現在可以管理多個輸入和輸出通道(channel)。
Channel
首先說一下 Channel,國內大多翻譯成“通道”。Channel 和 IO 中的 Stream(流)是差不多一個 等級的。只不過 Stream 是單向的,譬如:InputStream, OutputStream,而 Channel 是雙向 的,既可以用來進行讀操作,又可以用來進行寫操作。
NIO 中的 Channel 的主要實現有:
-
-
FileChannel
-
-
-
DatagramChannel
-
-
-
SocketChannel
-
-
-
ServerSocketChannel
-
這里看名字就可以猜出個所以然來:分別可以對應文件 IO、UDP 和 TCP(Server 和 Client)。下面演示的案例基本上就是圍繞這 4 個類型的 Channel 進行陳述的。
Buffer
Buffer,故名思意,緩沖區,實際上是一個容器,是一個連續數組。Channel 提供從文件、 網絡讀取數據的渠道,但是讀取或寫入的數據都必須經由 Buffer。
上面的圖描述了從一個客戶端向服務端發送數據,然后服務端接收數據的過程。客戶端發送數據時,必須先將數據存入 Buffer 中,然后將 Buffer 中的內容寫入通道。服務端這邊接收數據必 須通過 Channel 將數據讀入到 Buffer 中,然后再從 Buffer 中取出數據來處理。
在 NIO 中,Buffer 是一個頂層父類,它是一個抽象類,常用的 Buffer 的子類有:ByteBuffer、IntBuffer、 CharBuffer、 LongBuffer、 DoubleBuffer、FloatBuffer、 ShortBuffer
Selector
Selector 類是 NIO 的核心類,Selector 能夠檢測多個注冊的通道上是否有事件發生,如果有事 件發生,便獲取事件然后針對每個事件進行相應的響應處理。這樣一來,只是用一個單線程就可 以管理多個通道,也就是管理多個連接。這樣使得只有在連接真正有讀寫事件發生時,才會調用 函數來進行讀寫,就大大地減少了系統開銷,并且不必為每個連接都創建一個線程,不用去維護 多個線程,并且避免了多線程之間的上下文切換導致的開銷。