小師妹學JavaIO之:NIO中Channel的妙用
阿新 • • 發佈:2020-06-11
[toc]
# 簡介
小師妹,你還記得我們使用IO和NIO的初心嗎?
小師妹:F師兄,使用IO和NIO不就是為了讓生活更美好,世界充滿愛嗎?讓我等程式設計師可以優雅的將資料從一個地方搬運到另外一個地方。利其器,善其事,才有更多的時間去享受生活呀。
善,如果將資料比做人,IO,NIO的目的就是把人運到美國。
小師妹:F師兄,為什麼要運到美國呀,美國現在新冠太嚴重了,還是待在中國吧。中國是世界上最安全的國家!
好吧,為了保險起見,我們要把人運到上海。人就是資料,怎麼運過去呢?可以坐飛機,坐汽車,坐火車,這些什麼飛機,汽車,火車就可以看做是一個一個的Buffer。
最後飛機的航線,汽車的公路和火車的軌道就可以看做是一個個的channel。
更多精彩內容且看:
* [區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新](http://www.flydean.com/blockchain/)
* [Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新](http://www.flydean.com/learn-spring-boot/)
* [Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新](http://www.flydean.com/spring5/)
* [java程式設計師從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程](https://blog.csdn.net/superfjj/article/details/105482751)
簡單點講,channel就是負責運送Buffer的通道。
![](https://img-blog.csdnimg.cn/20200520145243522.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70)
IO按源頭來分,可以分為兩種,從檔案來的File IO,從Stream來的Stream IO。不管哪種IO,都可以通過channel來運送資料。
# Channel的分類
雖然資料的來源只有兩種,但是JDK中Channel的分類可不少,如下圖所示:
![](https://img-blog.csdnimg.cn/20200514143225602.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70)
先來看看最基本的,也是最頂層的介面Channel:
~~~java
public interface Channel extends Closeable {
public boolean isOpen();
public void close() throws IOException;
}
~~~
最頂層的Channel很簡單,繼承了Closeable介面,需要實現兩個方法isOpen和close。
一個用來判斷channel是否開啟,一個用來關閉channel。
小師妹:F師兄,頂層的Channel怎麼這麼簡單,完全不符合Channel很複雜的人設啊。
別急,JDK這麼做其實也是有道理的,因為是頂層的介面,必須要更加抽象更加通用,結果,一通用就發現還真的就只有這麼兩個方法是通用的。
所以為了應對這個問題,Channel中定義了很多種不同的型別。
最最底層的Channel有5大型別,分別是:
## FileChannel
這5大channel中,和檔案File有關的就是這個FileChannel了。
FileChannel可以從RandomAccessFile, FileInputStream或者FileOutputStream中通過呼叫getChannel()來得到。
也可以直接呼叫FileChannel中的open方法傳入Path建立。
~~~java
public abstract class FileChannel
extends AbstractInterruptibleChannel
implements SeekableByteChannel, GatheringByteChannel, ScatteringByteChannel
~~~
我們看下FileChannel繼承或者實現的介面和類。
AbstractInterruptibleChannel實現了InterruptibleChannel介面,interrupt大家都知道吧,用來中斷執行緒執行的利器。來看一下下面一段非常玄妙的程式碼:
~~~java
protected final void begin() {
if (interruptor == null) {
interruptor = new Interruptible() {
public void interrupt(Thread target) {
synchronized (closeLock) {
if (closed)
return;
closed = true;
interrupted = target;
try {
AbstractInterruptibleChannel.this.implCloseChannel();
} catch (IOException x) { }
}
}};
}
blockedOn(interruptor);
Thread me = Thread.currentThread();
if (me.isInterrupted())
interruptor.interrupt(me);
}
~~~
上面這段程式碼就是AbstractInterruptibleChannel的核心所在。
首先定義了一個Interruptible的例項,這個例項中有一個interrupt方法,用來關閉Channel。
然後獲得當前執行緒的例項,判斷當前執行緒是否Interrupted,如果是的話,就呼叫Interruptible的interrupt方法將當前channel關閉。
SeekableByteChannel用來連線Entry或者File。它有一個獨特的屬性叫做position,表示當前讀取的位置。可以被修改。
GatheringByteChannel和ScatteringByteChannel表示可以一次讀寫一個Buffer序列結合(Buffer Array):
~~~java
public long write(ByteBuffer[] srcs, int offset, int length)
throws IOException;
public long read(ByteBuffer[] dsts, int offset, int length)
throws IOException;
~~~
## Selector和Channel
在講其他幾個Channel之前,我們看一個和下面幾個channel相關的Selector:
![](https://img-blog.csdnimg.cn/20200520142919874.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70)
這裡要介紹一個新的Channel型別叫做SelectableChannel,之前的FileChannel的連線是一對一的,也就是說一個channel要對應一個處理的執行緒。而SelectableChannel則是一對多的,也就是說一個處理執行緒可以通過Selector來對應處理多個channel。
SelectableChannel通過註冊不同的SelectionKey,實現對多個Channel的監聽。後面我們會具體的講解Selector的使用,敬請期待。
## DatagramChannel
DatagramChannel是用來處理UDP的Channel。它自帶了Open方法來建立例項。
來看看DatagramChannel的定義:
~~~java
public abstract class DatagramChannel
extends AbstractSelectableChannel
implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, MulticastChannel
~~~
ByteChannel表示它同時是ReadableByteChannel也是WritableByteChannel,可以同時寫入和讀取。
MulticastChannel代表的是一種多播協議。正好和UDP對應。
## SocketChannel
SocketChannel是用來處理TCP的channel。它也是通過Open方法來建立的。
~~~java
public abstract class SocketChannel
extends AbstractSelectableChannel
implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel
~~~
SocketChannel跟DatagramChannel的唯一不同之處就是實現的是NetworkChannel藉口。
NetworkChannel提供了一些network socket的操作,比如繫結地址等。
## ServerSocketChannel
ServerSocketChannel也是一個NetworkChannel,它主要用在伺服器端的監聽。
~~~java
public abstract class ServerSocketChannel
extends AbstractSelectableChannel
implements NetworkChannel
~~~
## AsynchronousSocketChannel
最後AsynchronousSocketChannel是一種非同步的Channel:
~~~java
public abstract class AsynchronousSocketChannel
implements AsynchronousByteChannel, NetworkChannel
~~~
為什麼是非同步呢?我們看一個方法:
~~~java
public abstract Future