你好,我是彤哥,本篇是netty系列的第五篇。 簡介 上一章我們一起學習瞭如何使用Java原生NIO實現群聊系統,這章我們一起來看看Java NIO的核心組件之一——Channel。 思維轉變 首先,我想說的最重要的一個點是,學習NIO思維一定要從BIO那種一個連接一個線程的模式轉變成多個連接(Ch ...
你好,我是彤哥,本篇是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
最後,也歡迎來我的工從號彤哥讀源碼系統地學習源碼&架構的知識。