文章目錄
- 一 通道
- 二 通道接口
- 三 接口說明
- 3.1 AutoCloseable
- 3.2 Closeable
- 3.3 Channel
- 2.4 InterruptibleChannel
- 2.5 ReadableByteChannel
- 2.6 ScatteringByteChannel
- 2.7 WritableByteChannel
- 2.8 GatheringByteChannel
- 2.9 ByteChannel
- 2.10 SeekableByteChannel
- 2.11 AsynchronousChannel
- 2.12 AsynchronousByteChannel
- 2.13 NetworkChannel
- 2.14 MulticastChannel
- 四 結語
一 通道
三章節的緩衝區介紹完畢後就開始進入下一個主題——通道。如果說緩衝區僅僅是客戶端操作緩衝數據的港口,那麼船舶和航線就是通道,通道負責將貨物(緩衝數據)從兩個港口間傳遞,已達港口的貨物或許會被消費到各個商店,或許會被押到船舶上送往下一個目的地。
單單講緩衝區是沒有意義的(哼,我不信,包裝數組就當是擴充API使用不行麼……),NIO需要解決的是數據如何在不同客戶端間流動,這是一個雙向問題,因此NIO在設計通道時,複雜度遠高於緩衝區設計。
但實際上真正應用到項目中的通道又十分有限,所以通道部分我會拆成兩個部分,一個是通道的接口設計,也就是本章要介紹的內容;另一個是NIO已實現的通道類型,並且做示意性的使用說明介紹。
二 通道接口
之所以先介紹接口,是因爲NIO對通道的設計進行了非常多的功能抽象,具體可用的通道類具備哪些特性都是由其實現的接口決定的,所以要想比較透徹的瞭解通道設計,必須先弄清楚整個NIO通道體系的接口設計。需要注意的是,雖然通道接口定義了實現類的行爲,但是其行爲的特性如何,還是要依具體實現決定,這部分內容在後續介紹可用通道類的時候再結合源碼設計來說。
整個NIO通道接口派生關係如下:
三 接口說明
這部分不會把接口的設計說明的太過詳細,實際上從接口的定義上並不能很明顯的看出接口方法的具體行爲,JDK也僅僅是在接口方法的註釋上給了一些實現類需要注意的點,我查了一些資料,絕大部分的書和網文也都是對這些接口方法的註釋翻譯了一下……
3.1 AutoCloseable
這個接口是JKD1.7引入的,用來支持try-catch-resources語法,解決finally塊中關閉異常時的異常覆蓋問題。這意味這所有Channel類均具備自動關閉的特性。
注意咯,我特意提到了一句話:finally塊中關閉異常時的異常覆蓋!這裏很多剛入門的朋友可能比較難理解,我具一個例子:
public class ExceptionTest implements AutoCloseable {
public static void main(String[] args) throws Exception {
ExceptionTest exceptionTest = null;
try {
exceptionTest = new ExceptionTest();
// 注意open會拋出異常
exceptionTest.open();
} finally {
try {
if (exceptionTest != null) {
// open異常後執行close,close繼續拋出異常
exceptionTest.close();
}
} catch (Exception e) {
// open的異常就丟失了
e.printStackTrace();
}
}
}
public void open() throws IOException {
System.out.println("invoke open method");
throw new IOException("open method exception");
}
public void close() throws Exception {
System.out.println("invoke close method");
throw new IOException("close method exception");
}
}
輸出結果如下:
invoke open method
invoke close method
java.io.IOException: close method exception
at com.demo.nio.ExceptionTest.close(ExceptionTest.java:36)
at com.demo.nio.ExceptionTest.main(ExceptionTest.java:19)
Exception in thread "main" java.io.IOException: open method exception
at com.demo.nio.ExceptionTest.open(ExceptionTest.java:31)
at com.demo.nio.ExceptionTest.main(ExceptionTest.java:14)
上面的示例程序你一定不陌生,JDBC處理連接關閉的時候常常會用到上面的寫法,實際上當關閉SQL連接時未必不會出現其他異常,那麼在finally中關閉資源時經常會丟失最初的異常信息,而try-catch-resource語法可以規避這個問題:
public class ExceptionTest implements AutoCloseable {
public static void main(String[] args) throws Exception {
try (ExceptionTest exceptionTest = new ExceptionTest()) {
exceptionTest.open();
}
}
public void open() throws IOException {
System.out.println("invoke open method");
throw new IOException("open method exception");
}
public void close() throws Exception {
System.out.println("invoke close method");
throw new IOException("close method exception");
}
}
輸出結果如下:
invoke open method
invoke close method
Exception in thread "main" java.io.IOException: open method exception
at com.demo.nio.ExceptionTest.open(ExceptionTest.java:31)
at com.demo.nio.ExceptionTest.main(ExceptionTest.java:8)
Suppressed: java.io.IOException: close method exception
at com.demo.nio.ExceptionTest.close(ExceptionTest.java:36)
at com.demo.nio.ExceptionTest.main(ExceptionTest.java:9)
從異常堆棧信息的對比很容易能看出來,使用try-catch-resource的方式不會丟失任何異常信息,並且這種語法也極大的化簡了編碼工作量。
3.2 Closeable
這是一個非常古老的接口,用於支持可顯式關閉的資源,JDK1.5版本加入,1.7版本擴展了父接口AutoCloseable。
這意味着所有的通道都支持自動關閉和顯式關閉。
3.3 Channel
這是真正意義上的通道接口,因爲通道必須支持關閉操作,那麼客戶端就必須時刻掌握當前時刻通道是否可用,因此Channel接口中提供了isOpen函數。
2.4 InterruptibleChannel
這個接口什麼方法都沒留下,僅僅留下一個繼承而來的close方法,然後補充了一些註釋內容,從註釋內容上看InterruptibleChannel表示一個可中斷的通道,如果應用線程阻塞在此類型的通道上,那麼當通道關閉時,會收到java.nio.channels.AsynchronousCloseException異常,以此來支持Java的中斷模型。
實際上接口的實現遠比接口的定義複雜:
- 如果當前線程在實現了此接口的通道上調用了阻塞方法,那麼其他線程跳用此通道的close方法的時候,當前線程會收到AsynchronousCloseException;
- 如果當前線程在實現了此接口的通道上調用了阻塞方法,那麼其他線程跳用此通道的interrupte方法時,通道會被關閉,當前線程也會收到AsynchronousCloseException,並且這個線程會一直處理中斷狀態,除非狀態被重置
2.5 ReadableByteChannel
提供了從通道讀取數據的方法,需要注意的是當前線程讀取數據時,其他線程的讀操作會被阻塞,並且涉及操作系統底層數據傳輸,因此僅支持字節讀取。
2.6 ScatteringByteChannel
這個接口作爲ReadableByteChannel的直接派生接口,擴展了字節讀的操作,允許將通道中的數據讀取到多個緩衝區中。
2.7 WritableByteChannel
和讀相對,這是字節寫入接口,和字節讀相同的是當前線程寫入時,其他線程的寫操作會被阻塞。
2.8 GatheringByteChannel
和ScatteringByteChannel類似,允許多個緩衝區向通道中寫入數據。
2.9 ByteChannel
這個接口從前文的接口關係UML圖中可以看出,結合了字節讀寫接口,此接口的實現類可以具備讀寫操作。
2.10 SeekableByteChannel
這個接口派生自ByteChannel,在讀寫操作的基礎上,擴充了對position的維護操作。
2.11 AsynchronousChannel
這是實現異步IO操作的核心接口,並且實現了此接口的通道是線程安全的,允許併發讀寫,但是需要注意的是不允許在一個IO未完成的情況下再次讀寫。
2.12 AsynchronousByteChannel
在AsynchronousChannel接口的基礎上擴充了以字節爲單位的讀寫操作。
2.13 NetworkChannel
有一個非常核心的接口,支持Socket的數據讀寫操作,將通道中的數據和Socket關聯,網絡NIO實現的最重要的接口。
2.14 MulticastChannel
這個接口稍微難理解一些,但是瞭解IP廣播的話就不難了,它的功能就是支持IP多路廣播的,將多個IP打包成一組,然後將報文向組內所有IP對應的主機發送。
四 結語
如果想關注更多硬技能的分享,可以參考積少成多系列傳送門,未來每一篇關於硬技能的分享都會在傳送門中更新鏈接。