Java NIO系列(一) - 概述
前言
Java NIO
全稱java non-blocking IO
,是指jdk1.4
及以上版本裏提供的新api
(New IO
),為所有的原始類型(boolean
類型除外)提供緩存支持的數據容器,使用它可以提供非阻塞式的高伸縮性網絡。
Java NIO
提供了與標準IO
不同的IO
工作方式,Channel
、Buffer
和Selector
構成了核心的API
。其它組件,如Pipe
和FileLock
,只不過是與三個核心組件共同使用的工具類。
- 通道和緩沖區 (
Channel and Buffer
):
標準的IO
基於字節流和字符流進行單向的數據讀寫操作。而NIO
是基於通道(Channel
)和緩沖區(Buffer
- 異步IO (
Asynchronous IO
):
Java NIO
可以讓你異步的使用IO
,例如:當線程從通道讀取數據到緩沖區時,線程還是可以進行其他事情;當數據被線程寫入到緩沖區時,線程可以繼續處理它。從緩沖區寫入通道也類似。
- 選擇器 (
Selector
):
Java NIO
引入了選擇器的概念,選擇器用於監聽多個通道的事件(比如:連接打開,數據讀取和數據寫入)。因此,單個的線程可以監聽多個數據通道。
下面就來詳細介紹Java NIO
的相關知識。
正文
1. Java NIO概述
Java NIO
由以下幾個核心部分組成:
- Channel
- Buffer
- Selector
1.1. Channel和Buffer
基本上,所有的IO
在
NIO中都從一個
Channel`開始:
通道Channel
有點像流(Stream
),兩者可以做個簡單對比:
- 流是單向的,一個流對象要麽是輸出流、要麽是輸入流。
- 通道是全雙工的,一個通道通常搭配緩存一起使用。數據可以從
Channel
讀到Buffer
中,也可以從Buffer
寫到Channel
中。
這裏有個圖示:
Channel和
Buffer`有好幾種類型。
JAVA NIO
中的一些主要Channel
的實現,主要涵蓋了文件IO
和UDP
、TCP
的網絡IO
:
- FileChannel:從文件
- ServerSocketChannel:能通過
UDP
讀寫網絡中的數據 - SocketChannel:能通過
TCP
讀寫網絡中的數據 - DatagramChannel:可以監聽新進來的
TCP
連接,像Web
服務器那樣。對每一個新進來的連接都會創建一個SocketChannel
。
JAVA NIO
中關鍵的Buffer
實現,涵蓋了除boolean
的其余7
種基本數據類型(byte
、short
、int
、long
、float
、double
和 char
):
- ByteBuffer
- CharBuffer
- ShortBuffer
- IntBuffer
- LongBuffer
- FloatBuffer
- DoubleBuffer
1.2. Selector
Selector
允許單線程處理多個Channel
的連接事件和數據讀寫。如果你的應用打開了多個連接(通道),但每個連接的流量都很低,使用Selector
就會很方便,例如:一個聊天服務器。
這是在一個單線程中使用一個Selector
處理3
個Channel
的圖示:
要使用Selector
,得向Selector
註冊Channel
,然後調用它的select()
方法。這個方法會一直阻塞到某個註冊的通道有事件就緒。一旦這個方法返回,線程就可以處理這些事件。
事件類型主要包括:新連接進來、數據接收、數據發送等。
2. Java NIO對比IO
上面提到了NIO
主要的組件和特性,在實際的IO
操作中,應該如何在標準IO
和NIO
進行選擇,這裏就需要具體對比兩者的差異,並引入一些概念。
IO | NIO | |
---|---|---|
底層讀寫實現 | 面向流讀寫 | 面向緩沖區讀寫 |
是否有選擇器 | 無 | 基於選擇器的事件分離 |
IO是否阻塞 | 阻塞式IO | 非阻塞式IO |
2.1. 底層讀寫實現
Java NIO
和IO
之間第一個最大的區別是,IO
是面向流的,NIO
是面向緩沖區的。
- 面向流
Java IO
面向流意味著每次從流中讀一個或多個字節,直至讀取所有字節,它們沒有被緩存在任何地方。此外,它不能前後移動流中的數據。如果需要前後移動從流中讀取的數據,需要先將它緩存到一個緩沖區。
- 面向緩沖區
Java NIO
的緩沖導向方法略有不同。數據讀取到一個它稍後處理的緩沖區,需要時可在緩沖區中前後移動。這就增加了處理過程中的靈活性。但是,還需要檢查是否該緩沖區中包含所有您需要處理的數據。而且,需確保當更多的數據讀入緩沖區時,不要覆蓋緩沖區裏尚未處理的數據。
2.2. 是否有選擇器
Java NIO
的選擇器允許一個單獨的線程來監視多個輸入通道。
IO的讀寫速度和CPU的處理速度相差了一個數量級,導致IO事件延長了CPU的空閑等待時間,導致性能上的瓶頸。為了盡量的縮短CPU的等待時間,在單個IO操作進行時CPU可以抽出身來去做別的事情(其他IO),
NIO
引入單線程處理多IO事件的概念,從而充分利用CPU分配的資源。
Java NIO
允許已註冊的多個通道使用一個選擇器,然後使用一個單獨的線程來選擇通道。這種選擇機制,使得一個單線程很容易地管理多個通道。
2.3. IO是否阻塞
所謂阻塞,就是線程在進行IO
操作時,不能抽出身來去幹其他事情,必須等待數據讀寫完成。
-
Java IO
的各種流是阻塞的。- 當一個線程調用
read()
或write()
時,該線程被阻塞,直到有一些數據被讀取,或數據完全寫入。該線程在此期間不能再幹任何事情。
- 當一個線程調用
-
Java NIO
的非阻塞的。- 當監聽某個通道的讀操作事件時,線程向該通道發送請求讀取數據,之後這個線程就可以去幹別的事情。
- 當監聽某個通道的寫操作事件時,線程向請求向該通道寫入數據,但不需要等待它完全寫入,這個線程同時可以去做別的事情。
線程通常將非阻塞
IO
的空閑時間用於在其它通道上執行IO操作,所以一個單獨的線程現在可以管理多個輸入和輸出通道(channel
)。
歡迎關註技術公眾號: 零壹技術棧
本帳號將持續分享後端技術幹貨,包括虛擬機基礎,多線程編程,高性能框架,異步、緩存和消息中間件,分布式和微服務,架構學習和進階等學習資料和文章。
Java NIO系列(一) - 概述