小师妹学JavaIO之:NIO中Channel的妙用
文章目錄
- 簡介
- 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 FileChannelextends AbstractInterruptibleChannelimplements 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 DatagramChannelextends AbstractSelectableChannelimplements ByteChannel, ScatteringByteChannel, GatheringByteChannel, MulticastChannelByteChannel表示它同時是ReadableByteChannel也是WritableByteChannel,可以同時寫入和讀取。
MulticastChannel代表的是一種多播協議。正好和UDP對應。
SocketChannel
SocketChannel是用來處理TCP的channel。它也是通過Open方法來創建的。
public abstract class SocketChannelextends AbstractSelectableChannelimplements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannelSocketChannel跟DatagramChannel的唯一不同之處就是實現的是NetworkChannel借口。
NetworkChannel提供了一些network socket的操作,比如綁定地址等。
ServerSocketChannel
ServerSocketChannel也是一個NetworkChannel,它主要用在服務器端的監聽。
public abstract class ServerSocketChannelextends AbstractSelectableChannelimplements NetworkChannelAsynchronousSocketChannel
最后AsynchronousSocketChannel是一種異步的Channel:
public abstract class AsynchronousSocketChannelimplements 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的博客
歡迎關注我的公眾號:程序那些事,更多精彩等著您!
總結
以上是生活随笔為你收集整理的小师妹学JavaIO之:NIO中Channel的妙用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小师妹学JavaIO之:Buffer和B
- 下一篇: 小师妹学JavaIO之:MappedBy