JAVA NIO程式設計入門(一)

木木匠發表於2018-09-01

一、前言

筆者之前接觸的NIO程式設計比較少,所以對這一塊的基礎也比較弱,NIO作為java程式設計中一個重要的模組,不能很好的掌握它,感覺自己在java方面就掌握的不夠,所以,接下來,筆者會學習NIO程式設計,所以,該系列文章不會涉及到很深原始碼解析,純粹的是學習課程,也可以理解為筆者的筆記,記錄學習NIO的過程,同時也希望這類文章可以對同樣想掌握NIO程式設計的你有幫助。

二、什麼是NIO?

Java NIO(New IO)是一個可以替代標準Java IO API的IO API(從Java 1.4開始),Java NIO提供了與標準IO不同的IO工作方式。NIO可以理解為非阻塞IO,傳統的IO的read和write只能阻塞執行,執行緒在讀寫IO期間不能幹其他事情,比如呼叫socket.read()時,如果伺服器一直沒有資料傳輸過來,執行緒就一直阻塞,而NIO中可以配置socket為非阻塞模式。

三、IO和NIO的區別

  • IO是面向位元組流和字元流的,而NIO是面向緩衝區的。
  • IO是阻塞模式的,NIO是非阻塞模式的
  • NIO新增了選擇器的概念,可以通過選擇器監聽多個通道。

四、NIO相關概念

Channel(通道)

Java NIO的通道類似流,但又有些不同:

既可以從通道中讀取資料,又可以寫資料到通道。但流的讀寫通常是單向的。 通道可以非同步地讀寫。

通道中的資料總是要先讀到一個Buffer,或者總是要從一個Buffer中寫入。

JAVA NIO程式設計入門(一)

Channel的實現

這些是Java NIO中最重要的通道的實現:

  • FileChannel: 從檔案中讀寫資料。

  • DatagramChannel : 能通過UDP讀寫網路中的資料。

  • SocketChannel: 能通過TCP讀寫網路中的資料。

  • ServerSocketChannel :可以監聽新進來的TCP連線,像Web伺服器那樣。對每一個新進來的連線都會建立一個SocketChannel。

Buffer(緩衝區)

緩衝區本質上是一塊可以寫入資料,然後可以從中讀取資料的記憶體。這塊記憶體被包裝成NIO Buffer物件,並提供了一組方法,用來方便的訪問該塊記憶體。

Buffer(緩衝區)的主要屬性

屬性 功能
capacity 容量
position 緩衝區當前位置指標,最大可為capacity – 1
limit 緩衝區最大讀取和寫入限制

buffer屬性示意圖:

JAVA NIO程式設計入門(一)

上圖展示了寫模式和讀模式下,以上屬性的示意圖,寫模式下,limit和capacity是一樣的,這表示你能寫入的最大容量資料,讀模式下,limit會和position一樣,表示你能讀到寫入的全部資料。

Buffer(緩衝區)的主要分類

  • ByteBuffer
  • MappedByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

其實以上基本上為了接收不同的資料型別而對應的,只有一個特殊的MappedByteBuffer,本次先不學習它,後續再去了解它。

五、實戰

我們主要以理解上面介紹的概念為目的實現一個簡單的NIO程式設計,讀取資料夾內的檔案,然後輸出到控制檯。

 public static  void testNio(){
        try {
            RandomAccessFile rdf=new RandomAccessFile("E:\\nio\\niotest.txt","rw");
            //利用channel中的FileChannel來實現檔案的讀取
            FileChannel inChannel=  rdf.getChannel(); 
            //設定緩衝區容量為10
            ByteBuffer buf=  ByteBuffer.allocate(10);
            //從通道中讀取資料到緩衝區,返回讀取的位元組數量
            int byteRead=inChannel.read(buf);
            //數量為-1表示讀取完畢。
            while (byteRead!=-1){
                //切換模式為讀模式,其實就是把postion位置設定為0,可以從0開始讀取
                buf.flip();
                //如果緩衝區還有資料
                while (buf.hasRemaining()){
                    //輸出一個字元
                    System.out.print((char) buf.get());
                }
                //資料讀完後清空緩衝區
                buf.clear();
                //繼續把通道內剩餘資料寫入緩衝區
                byteRead = inChannel.read(buf);
            }
            //關閉通道
            rdf.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
複製程式碼

我在磁碟中niotest.txt的內容是hello world,我們看看執行結果是不是hello world。

JAVA NIO程式設計入門(一)
結果和預期一樣,我們完成了NIO程式設計輸出磁碟檔案內容,其實上面程式碼中,我第一印象有疑問的地方就是buf.flip(),其他只要用過IO的應該都能理解,既然不能理解我們就先除錯下,註釋掉該段內容,看看輸出結果如何。

JAVA NIO程式設計入門(一)

註釋掉buf.flip()後,輸出結果果真不對了,那究竟是為啥呢?

JAVA NIO程式設計入門(一)
上圖是第一個迴圈,我們可以看到緩衝區的position=10,limit=10,cap=10。呼叫buf.hasRemaining()為false,所以buffer第一次沒有輸出任何東西。看看第二次迴圈

JAVA NIO程式設計入門(一)
其實這整個迴圈可以解析步驟如下:

  • buffer讀取了10位元組內容,內容就是:hello worl。

  • buf.hasRemaining() 判斷為false,直接清空buffer(直接把position復位為0,可以直接覆蓋內容),讀取剩下內容。

  • 最後只剩下一個字元 d 讀取,這個時候讀取完後,buffer內容如下:dello worl。

  • 最後輸出因為從postion=1位置輸出,所以輸出:ello worl。

根據以上解析,我們發現註釋了buf.flip()後,position位置寫入是什麼位置,讀出就是什麼位置,所以,該方法應該就是把position賦值為0,從開始讀取。解讀原始碼也證實了我的猜想,原始碼如下:

  public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }
複製程式碼

參考

JAVA NIO

推薦閱讀

Java鎖之ReentrantLock(一)

Java鎖之ReentrantLock(二)

Java鎖之ReentrantReadWriteLock

相關文章