目錄
- 簡介
- Channel的分類
- FileChannel
- Selector和Channel
- DatagramChannel
- SocketChannel
- ServerSocketChannel
- AsynchronousSocketChannel
- 使用Channel
- 總結
簡介
小師妹,你還記得我們使用IO和NIO的初心嗎?
小師妹:F師兄,使用IO和NIO不就是為了讓生活更美好,世界充滿愛嗎?讓我等程序員可以優雅的將數據從一個地方搬運到另外一個地方。利其器,善其事,才有更多的時間去享受生活呀。
善,如果將數據比做人,IO,NIO的目的就是把人運到美國。
小師妹:F師兄,為什麼要運到美國呀,美國現在新冠太嚴重了,還是待在中國吧。中國是世界上最安全的國家!
好吧,為了保險起見,我們要把人運到上海。人就是數據,怎麼運過去呢?可以坐飛機,坐汽車,坐火車,這些什麼飛機,汽車,火車就可以看做是一個一個的Buffer。
最後飛機的航線,汽車的公路和火車的軌道就可以看做是一個個的channel。
更多精彩內容且看:
- 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
- Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
- Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
- java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程
簡單點講,channel就是負責運送Buffer的通道。
IO按源頭來分,可以分為兩種,從文件來的File IO,從Stream來的Stream IO。不管哪種IO,都可以通過channel來運送數據。
Channel的分類
雖然數據的來源只有兩種,但是JDK中Channel的分類可不少,如下圖所示:
先來看看最基本的,也是最頂層的接口Channel:
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創建。
public abstract class FileChannel
extends AbstractInterruptibleChannel
implements SeekableByteChannel, GatheringByteChannel, ScatteringByteChannel
我們看下FileChannel繼承或者實現的接口和類。
AbstractInterruptibleChannel實現了InterruptibleChannel接口,interrupt大家都知道吧,用來中斷線程執行的利器。來看一下下面一段非常玄妙的代碼:
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):
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:
這裏要介紹一個新的Channel類型叫做SelectableChannel,之前的FileChannel的連接是一對一的,也就是說一個channel要對應一個處理的線程。而SelectableChannel則是一對多的,也就是說一個處理線程可以通過Selector來對應處理多個channel。
SelectableChannel通過註冊不同的SelectionKey,實現對多個Channel的監聽。後面我們會具體的講解Selector的使用,敬請期待。
DatagramChannel
DatagramChannel是用來處理UDP的Channel。它自帶了Open方法來創建實例。
來看看DatagramChannel的定義:
public abstract class DatagramChannel
extends AbstractSelectableChannel
implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, MulticastChannel
ByteChannel表示它同時是ReadableByteChannel也是WritableByteChannel,可以同時寫入和讀取。
MulticastChannel代表的是一種多播協議。正好和UDP對應。
SocketChannel
SocketChannel是用來處理TCP的channel。它也是通過Open方法來創建的。
public abstract class SocketChannel
extends AbstractSelectableChannel
implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel
SocketChannel跟DatagramChannel的唯一不同之處就是實現的是NetworkChannel借口。
NetworkChannel提供了一些network socket的操作,比如綁定地址等。
ServerSocketChannel
ServerSocketChannel也是一個NetworkChannel,它主要用在服務器端的監聽。
public abstract class ServerSocketChannel
extends AbstractSelectableChannel
implements NetworkChannel
AsynchronousSocketChannel
最後AsynchronousSocketChannel是一種異步的Channel:
public abstract class AsynchronousSocketChannel
implements AsynchronousByteChannel, NetworkChannel
為什麼是異步呢?我們看一個方法:
public abstract Future<Integer> read(ByteBuffer dst);
可以看到返回值是一個Future,所以read方法可以立刻返回,只在我們需要的時候從Future中取值即可。
使用Channel
小師妹:F師兄,講了這麼多種類的Channel,看得我眼花繚亂,能不能講一個Channel的具體例子呢?
好的小師妹,我們現在講一個使用Channel進行文件拷貝的例子,雖然Channel提供了transferTo的方法可以非常簡單的進行拷貝,但是為了能夠看清楚Channel的通用使用,我們選擇一個更加常規的例子:
public void useChannelCopy() throws IOException {
FileInputStream input = new FileInputStream ("src/main/resources/www.flydean.com");
FileOutputStream output = new FileOutputStream ("src/main/resources/www.flydean.com.txt");
try(ReadableByteChannel source = input.getChannel(); WritableByteChannel dest = output.getChannel()){
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
while (source.read(buffer) != -1)
{
// flip buffer,準備寫入
buffer.flip();
// 查看是否有更多的內容
while (buffer.hasRemaining())
{
dest.write(buffer);
}
// clear buffer,供下一次使用
buffer.clear();
}
}
}
上面的例子中我們從InputStream中讀取Buffer,然後寫入到FileOutputStream。
總結
今天講解了Channel的具體分類,和一個簡單的例子,後面我們會再體驗一下Channel的其他例子,敬請期待。
本文的例子https://github.com/ddean2009/learn-java-io-nio
本文作者:flydean程序那些事
本文鏈接:http://www.flydean.com/java-io-nio-channel/
本文來源:flydean的博客
歡迎關注我的公眾號:程序那些事,更多精彩等着您!
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※帶您來了解什麼是 USB CONNECTOR ?
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!
※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※教你寫出一流的銷售文案?