NIO 四 通道接口

一 通道

  三章節的緩衝區介紹完畢後就開始進入下一個主題——通道。如果說緩衝區僅僅是客戶端操作緩衝數據的港口,那麼船舶和航線就是通道,通道負責將貨物(緩衝數據)從兩個港口間傳遞,已達港口的貨物或許會被消費到各個商店,或許會被押到船舶上送往下一個目的地。

  單單講緩衝區是沒有意義的(哼,我不信,包裝數組就當是擴充API使用不行麼……),NIO需要解決的是數據如何在不同客戶端間流動,這是一個雙向問題,因此NIO在設計通道時,複雜度遠高於緩衝區設計。

  但實際上真正應用到項目中的通道又十分有限,所以通道部分我會拆成兩個部分,一個是通道的接口設計,也就是本章要介紹的內容;另一個是NIO已實現的通道類型,並且做示意性的使用說明介紹。

二 通道接口

  之所以先介紹接口,是因爲NIO對通道的設計進行了非常多的功能抽象,具體可用的通道類具備哪些特性都是由其實現的接口決定的,所以要想比較透徹的瞭解通道設計,必須先弄清楚整個NIO通道體系的接口設計。需要注意的是,雖然通道接口定義了實現類的行爲,但是其行爲的特性如何,還是要依具體實現決定,這部分內容在後續介紹可用通道類的時候再結合源碼設計來說。

  整個NIO通道接口派生關係如下:

NIO通道接口UML

三 接口說明

  這部分不會把接口的設計說明的太過詳細,實際上從接口的定義上並不能很明顯的看出接口方法的具體行爲,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的中斷模型。

  實際上接口的實現遠比接口的定義複雜:

  1. 如果當前線程在實現了此接口的通道上調用了阻塞方法,那麼其他線程跳用此通道的close方法的時候,當前線程會收到AsynchronousCloseException;
  2. 如果當前線程在實現了此接口的通道上調用了阻塞方法,那麼其他線程跳用此通道的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對應的主機發送。

四 結語

  如果想關注更多硬技能的分享,可以參考積少成多系列傳送門,未來每一篇關於硬技能的分享都會在傳送門中更新鏈接。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章