Java關於NIO類的詳解


一、IO與NIO的區別:

  前提我們先說一說java IO: 

  Java中使用IO(輸入輸出)來讀取和寫入,讀寫設備上的數據、硬盤文件、內存、鍵盤......,根據數據的走向可分為輸入流輸出流,這個走向是以內存為基准的,即往內存中讀數據是輸入流,從內存中往外寫是輸出流

  根據處理的數據類型可分為字節流和字符流:

  1.字節流可以處理所有數據類型的數據,在java中以Stream結尾

  2.字符流處理文本數據,在java中以ReaderWriter結尾

我們來看個IO流的詳解圖:

 

  1.NIO的起源:

    java.NIO是jdk1.4是提出,jdk1.7進行二次改進的java新型IO類,其最大的特點就是它的非阻塞性。

  2.IO與NIO結構差異:

    普通的IO是面向流(Stream Oriented),而NIO則是面向緩沖區(Buffer Oriented)。IO流是單向的,直接面向字節流,通過InputStream、OutputStream來完成數據的輸入輸出。而NIO是雙向的,通過建立通道Channel),然后將數據裝在緩沖區Buffer)在通道上進行傳輸。針對不同類型的數據有不同的Buffer,根據數據類型不同(boolean 除外),提供了相應類型的緩沖區: ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、 FloatBuffer、DoubleBuffer

 

二、NIO的Buffer的數據存取:

  1.創建Buffer實例:

ByteBuffer buf = ByteBuffer.allocate(1024); //1024為capacity,通過allocate()方法可以獲取一個緩沖區

  2.Buffer類屬性的解析:

    Buffer類中有三個屬性必須理解:capacity容量)、limit(訪問范圍)、position位置,表示緩沖區中正在操作數據的位置)。通過get(),put()方法進行數據的存取。

    通過flip()方法切換成讀模式,clear()方法清空緩沖區。但是緩沖區中的數據依然存在,但是處於“被遺忘”狀態,rewind()可重復讀。

  3.Buffer的分類:

    Buffer分為直接緩沖區非直接緩沖區。非直接緩沖區 通過allocate()方法分配緩沖區,將緩沖區建立在JVM的內存中。直接緩沖區 通過allocateDirect()方法,將緩沖區建立在物理內存中。這樣做可以提高IO效率,節省了copy的過程,直接緩沖區是物理內存映射文件,但是寫入過程不受控制,讀過程受GC影響!

三、通道的原理與獲取:

  1.通道的原理:

    傳統的javaIO是通過DMA的方式存取,這種方式需要CPU的權限。而通道Channel)自帶處理器,不需要去訪問CPU,所以在進行大量IO時效率更高一些。通道用於源節點與目標節點的連接。在 Java NIO 中負責緩沖區中數據的傳輸。Channel 本身不存儲數據,因此需要配合緩沖區進行傳輸。

  2.通道的獲取:

    通道的主要實現類 java.nio.channels.Channel 接口:FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel。

    獲取通道的方法:

      1)各IO有自己的獲取方法、

      2)jdk1.7的NIO2,針對各個通道提供了靜態方法open();

      3)jdk1.7的NIO2的Files工具類的newByteChannel();

四、通道的數據傳輸:

  1.分散(Scatter)與聚集(Gather):  

    1)分散讀取Scattering Reads),將Channel中讀取的數據分散到Buffer
    2)聚集寫入Gathering Writes),將多個Buffer中的數據聚集到Channel中

  2.實例代碼:

        RandomAccessFile raf1 = new RandomAccessFile("1.txt", "rw");       
        //1. 獲取通道
        FileChannel channel = raf1.getChannel();     
        //2. 分配指定大小的緩沖區
        ByteBuffer buf1 = ByteBuffer.allocate(250);
        ByteBuffer buf2 = ByteBuffer.allocate(500);        
        //3. 分散讀取
        ByteBuffer[] bufs = {buf1, buf2};
        channel1.read(bufs);      
        for (ByteBuffer byteBuffer : bufs) {
            byteBuffer.flip();
        }
        System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
        System.out.println("-----------------");
        System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));       
        //4. 聚集寫入
        RandomAccessFile raf2 = new RandomAccessFile("2.txt", "rw");
        FileChannel channel2 = raf2.getChannel();   
        channel2.write(bufs);

 

五、字符集 Charset:

  編碼:字符串->字節數組。解碼:字節數組->字符串

  1.查看Charset里都有哪些編碼:

        Map<String, Charset> map = Charset.availableCharsets();
        map.forEach((k,v)->{
            System.out.println(k);//常見的UTF-8等等..
        });

  2.緩沖區編解碼:

        Charset c = Charset.forName("UTF-8");
        CharsetEncoder e = cs1.newEncoder();  //獲取編碼器
        CharsetDecoder d = cs1.newDecoder();  //獲取解碼器    
        CharBuffer buf = CharBuffer.allocate(1024);
        buf.put("二狗子到此一游");
        buf.flip();
        ByteBuffer bBuf = e.encode(buf );//編碼
        bBuf.flip();                     //解碼
        CharBuffer buf2= d.decode(bBuf);
        System.out.println(buf2.toString());    

六、阻塞與非阻塞:

  阻塞與非阻塞式的,相較於網絡通信:
    1)阻塞:C端發送S端讀寫請求,S端考慮,阻塞,服務端不會做其他事情,解決方式多線程
    2)非阻塞:Selector(選擇器),通道會注冊到選擇器上,Selector監控IO狀況,只有C端准備就緒


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM