1. 程式人生 > >JAVA IO : BIO NIO AIO

JAVA IO : BIO NIO AIO

JAVA IO : BIO NIO AIO

同步非同步、阻塞非阻塞概念

同步和非同步是針對應用程式和核心的互動而言的,主要看是主動輪訓核心還是等待核心通知。 阻塞和非阻塞是針對於程序在訪問資料的時候,根據IO操作的就緒狀態來採取的不同方式,說白了是一種讀取或者寫入操作函式的實現方式,阻塞方式下讀取或者寫入函式將一直等待,而非阻塞方式下,讀取或者寫入函式會立即返回一個狀態值。

同步與非同步

  • 同步:使用者程序觸發IO操作並等待或者輪詢的去檢視IO操作是否就緒。餐廳視窗點了餐品,每過一會來問一下,我們的餐品是否準備好了。
  • 非同步:指使用者程序觸發IO操作以後便開始做自己的事情,而當IO操作已經完成的時候會得到IO完成的通知(非同步的特點就是通知)。餐廳視窗點餐,當說明我們需要的餐品之後,然後自己可以去幹別的事。當餐廳準備好了之後會通知我們。(使用非同步IO時,Java將IO讀寫委託給OS處理,需要將資料緩衝區地址和大小傳給OS) 。

阻塞與非阻塞

  • 阻塞:當試圖對該檔案描述符進行讀寫時, 如果當時沒有東西可讀,或者暫時不可寫, 程式就進入等待 狀態, 直到有東西可讀或者可寫為止。餐廳視窗點餐,當說明我們需要的餐品之後,視窗只會在加工完成並且送出我們需要的餐品的時候才會響應我們,在這期間,不可以做其他的事情,我們必須保持等待。

  • 非阻塞:當試圖對該檔案描述符進行讀寫時, 如果沒有東西可讀, 或者不可寫, 讀寫函式馬上返回, 而不會等待。餐廳視窗點餐,當說明我們需要的餐品之後,視窗立刻響應,給我們一張小票,領票完後我們自己可以玩玩手機,或者與別人聊聊天,我們可以輪詢(同步)我們的餐品是否OK或者等待通知(非同步)。

IO VS NIO VS AIO

IO NIO AIO(NIO2)
面向流 面向緩衝 面向緩衝
阻塞IO 非阻塞IO 非阻塞IO
同步 同步 非同步
選擇器 觀察者

面向流與面向緩衝

Java NIO和IO之間第一個最大的區別是,IO是面向流的,NIO是面向緩衝區的。 Java IO面向流意味著每次從流中讀一個或多個位元組,直至讀取所有位元組,它們沒有被快取在任何地方。此外,它不能前後移動流中的資料。如果需要前後移動從流中讀取的資料,需要先將它快取到一個緩衝區。 Java NIO的緩衝導向方法略有不同。資料讀取到一個它稍後處理的緩衝區,需要時可在緩衝區中前後移動。這就增加了處理過程中的靈活性。但是,還需要檢查是否該緩衝區中包含所有您需要處理的資料。而且,需確保當更多的資料讀入緩衝區時,不要覆蓋緩衝區裡尚未處理的資料。

阻塞與非阻塞IO

Java IO的各種流是阻塞的。這意味著,當一個執行緒呼叫read()write()時,該執行緒被阻塞,直到有一些資料被讀取,或資料完全寫入。該執行緒在此期間不能再幹任何事情了。 Java NIO的非阻塞模式,使一個執行緒從某通道傳送請求讀取資料,但是它僅能得到目前可用的資料,如果目前沒有資料可用時,就什麼都不會獲取。而不是保持執行緒阻塞,所以直至資料變的可以讀取之前,該執行緒可以繼續做其他的事情。 非阻塞寫也是如此。一個執行緒請求寫入一些資料到某通道,但不需要等待它完全寫入,這個執行緒同時可以去做別的事情。 執行緒通常將非阻塞IO的空閒時間用於在其它通道上執行IO操作,所以一個單獨的執行緒現在可以管理多個輸入和輸出通道(channel)。

在IO設計中,我們從InputStream或 Reader逐位元組讀取資料。假設你正在處理一基於行的文字資料流,例如:

Name: Anna
Age: 25
Email: [email protected].com
Phone: 1234567890

該文字行的流可以這樣處理:

        InputStream input = null;
        try {
            input = new FileInputStream("/data/file/temp/test.txt");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        BufferedReader reader = new BufferedReader(new InputStreamReader(input));

        try {
        	//堵塞到讀取本行資料完成
            String nameLine   = reader.readLine();
            //堵塞到讀取本行資料完成
            String ageLine    = reader.readLine();
            //堵塞到讀取本行資料完成
            String emailLine  = reader.readLine();
            //堵塞到讀取本行資料完成
            String phoneLine  = reader.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }

一旦reader.readLine()方法返回,你就知道肯定文字行就已讀完, readline()阻塞直到整行讀完,這就是原因。你也知道此行包含名稱;同樣,第二個readline()呼叫返回的時候,你知道這行包含年齡等。 正如你可以看到,該處理程式僅在有新資料讀入時執行,並知道每步的資料是什麼。一旦正在執行的執行緒已處理過讀入的某些資料,該執行緒不會再回退資料(大多如此)。

而一個NIO的實現會有所不同,下面是一個簡單的例子:

        RandomAccessFile aFile = null;
        try {
            aFile = new RandomAccessFile("/data/file/temp/big.txt", "rw");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        FileChannel inChannel = aFile.getChannel();

        //建造一塊20480位元組的大緩衝區
        ByteBuffer buf = ByteBuffer.allocate(20480);

        //寫入檔案資訊到緩衝區
        int bytesRead = 0;
        try {
        	//不會堵塞,立刻返回
            bytesRead = inChannel.read(buf);
            //檢視已經讀取到快取的位元組數
            System.out.println(bytesRead);
            while (bytesRead != -1) {
                //轉為讀取模式
                buf.flip();
                while(buf.hasRemaining()){
                    // 一次讀取一個位元組
                    System.out.print((char) buf.get());
                }
                //清空緩衝區
                buf.clear();
                //繼續寫入檔案資訊到緩衝區
                bytesRead = inChannel.read(buf);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                aFile.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }           

從通道讀取位元組到ByteBuffer。當這個方法呼叫返回時,你不知道你所需的所有資料是否全部載入完成是否都已經在緩衝區內。
結果如下
讀取位元組數

BIO、NIO、AIO的JAVA實現

  • Java BIO: 同步並阻塞,伺服器實現模式為一個連線一個執行緒,即客戶端有連線請求時伺服器端就需要啟動一個執行緒進行處理,如果這個連線不做任何事情會造成不必要的執行緒開銷,當然可以通過執行緒池機制改善。
  • Java NIO: 同步非阻塞,伺服器實現模式為一個請求一個執行緒,即客戶端傳送的連線請求都會註冊到多路複用器(selector)上,多路複用器輪詢到連線有I/O請求時才啟動一個執行緒進行處理。
  • Java AIO(NIO.2): 非同步非阻塞,伺服器實現模式為一個有效請求一個執行緒,客戶端的I/O請求都是由OS先完成了再通知伺服器應用去啟動執行緒進行處理,

BIO、NIO、AIO適用場景分析

  • BIO方式適用於連線數目比較小且固定的架構,這種方式對伺服器資源要求比較高,併發侷限於應用中,JDK1.4以前的唯一選擇,但程式直觀簡單易理解。
  • NIO方式適用於連線數目多且連線比較短(輕操作)的架構,比如聊天伺服器,併發侷限於應用中,程式設計比較複雜,JDK1.4開始支援。
  • AIO方式使用於連線數目多且連線比較長(重操作)的架構,比如相簿伺服器,充分呼叫OS參與併發操作,程式設計比較複雜,JDK7開始支援。

另外,I/O屬於底層操作,需要作業系統支援,併發也需要作業系統的支援,所以效能方面不同作業系統差異會比較明顯。