5. 彤哥說netty系列之Java NIO核心元件之Channel
你好,我是彤哥,本篇是netty系列的第五篇。
簡介
上一章我們一起學習瞭如何使用Java原生NIO實現群聊系統,這章我們一起來看看Java NIO的核心元件之一——Channel。
思維轉變
首先,我想說的最重要的一個點是,學習NIO思維一定要從BIO那種一個連線一個執行緒的模式轉變成多個連線(Channel)共用一個執行緒來處理的這種思維。
1個Connection = 1個Socket = 1個Channel,這幾個概念可以看作是等價的,都表示一個連線,只不過是用在不同的場景中。
如果單從阻塞/非阻塞的角度來看的話,IO可以分成兩大類,一類是Blocking IO,一類是Non-blocking IO,像IO五種模型中的後四種其實都可以看作是非阻塞型IO,只是各自使用的手段不相同罷了。
在Java中,我們說的非阻塞IO或者說NIO(New IO)主要是指多路複用IO,底層可以使用select/poll/epoll等技術實現。
另外,在Java1.7的時候引入了NIO2,這個主要是指非同步IO模型,也就是我們常說的AIO,底層完全使用非同步回撥的方式來實現。
但是,由於AIO這項技術在linux作業系統上還不太成熟,所以我們通常也不會說太多關於這方面的內容。
在後面我們學習Netty的時候會再次講到這三種IO模型,可以看到Netty是完全支援三種IO的,但是它把OIO(BIO)和AIO都給deprecated了,也進一步說明瞭AIO的不成熟性。
好了,扯了一下思維轉變的問題,下面正式進入今天的內容——Java NIO核心元件之Channel
Channel
概念
我們先來看看Java中對於Channel的定義,位於java.nio.channels.Channel類的註釋上:
A nexus for I/O operations.
本文來源工從號彤哥讀原始碼
A channel represents an open connection to an entity such as a hardware device,a file,a network socket,or a program component that is capable of performing one or more distinct I/O operations,for example reading or writing.
第一句,它是IO操作的一種連線。
nexus,the means of connection between things linked in series.
第二句,Channel代表到實體的開放連線,這個實體可以是硬體,檔案,網路套接字,或者程式元件,並且可以執行一個或多個不同的IO操作,例如,讀或寫。
簡單點講,Channel就是實體與實體之間的連線,比如,操作檔案可以使用FileChannel,操作網路可以使用SocketChannel等。
與流的區別
BIO是面向流(Stream)程式設計的,流又分成InputStream和OutputStream,那麼Channel和Stream有什麼區別呢?
- Channel可以同時支援讀和寫,而Stream只能支援單向的讀或寫(所以分成InputStream和OutputStream)
- Channel支援非同步讀寫,Stream通常只支援同步
- Channel總是讀向(read into)Buffer,或者寫自(write from)Buffer(有點繞,以Channel為中心,從Channel中讀出資料到Buffer,從Buffer中往Channel寫入資料)
實現方式
下面列舉了JDK中比較重要的實現方式:
- FileChannel:操作檔案
- DatagramChannel:UDP協議支援
- SocketChannel:TCP協議支援
- ServerSocketChannel:監聽TCP協議Accept事件,之後建立SocketChannel
例子
public class FileChannelTest {
public static void main(String[] args) throws IOException {
// 從檔案獲取一個FileChannel
FileChannel fileChannel = new RandomAccessFile("D:\\object.txt","rw").getChannel();
// 宣告一個Byte型別的Buffer
ByteBuffer buffer = ByteBuffer.allocate(10);
// 將FileChannel中的資料讀出到buffer中,-1表示讀取完畢
// buffer預設為寫模式,本文來源工從號彤哥讀原始碼
// read()方法是相對channel而言的,相對buffer就是寫
while ((fileChannel.read(buffer)) != -1) {
// buffer切換為讀模式
buffer.flip();
// buffer中是否有未讀資料
while (buffer.hasRemaining()) {
// 未讀資料的長度
int remain = buffer.remaining();
// 宣告一個位元組陣列
byte[] bytes = new byte[remain];
// 將buffer中資料讀出到位元組陣列中
buffer.get(bytes);
// 打印出來
System.out.println(new String(bytes,StandardCharsets.UTF_8));
}
// 清空buffer,為下一次寫入資料做準備
// clear()會將buffer再次切換為寫模式
buffer.clear();
}
}
}複製程式碼
可以看到,Channel與Buffer是息息相關的。注意這裡的讀寫方法,呼叫者是誰就以誰為核心,channel.read()就表示從channel讀出資料,buffer.get()就表示從buffer讀出資料,這跟傳統程式設計的角度不太一樣的地方。
總結
今天我們學習了Java NIO核心元件之Channel,它與傳統BIO中的流很類似但又有所區別,且經常與Buffer聯合起來使用,Buffer又是什麼呢?請聽下回分解。
參考
挺不錯的一個網站:
http://tutorials.jenkov.com/java-nio/channels.html
最後,也歡迎來我的工從號彤哥讀原始碼系統地學習原始碼&架構的知識。