Java〖NIO上篇〗看這一篇就夠了 緩衝區 通道

參考課程

PS: 之前一直想了解這個NIO到底是什麼東西,奈何目前用不到,聽說現在許多框架都在用,而且面試的時候也有被問道,感覺還是去多瞭解瞭解底層怎麼實現的~~這是我的第100篇博客!!!

一. NIO與IO區別

在這裏插入圖片描述
NIO主要有三大核心部分:Channel(通道),Buffer(緩衝區), Selector

二. 緩衝區

在這裏插入圖片描述

在這裏插入圖片描述

緩衝區(Buffer) :一個用於特定基本數據類型的容器。由 java.nio 包定義的,所有緩衝區都是 Buffer 抽象類的子類。
Java NIO 中的 Buffer 主要用於與 NIO 通道進行交互,數據是從通道讀入緩衝區,從緩衝區寫入通道中的。

2.1 直接緩衝區與非直接緩衝區

  • 非直接緩衝區:通過allocate()方法分配緩衝區,將緩衝區建立在JVM的內存中
  • 直接緩衝區:通過allocateDirect()方法分配直接緩衝區,將緩衝區建立在物理內存中。可以提高效率

在這裏插入圖片描述
這裏探究驗證了一下底層Buffer的原理

  //分配指定大小的緩衝區
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        String s=new String("abcde");
        //緩衝區存數據
        System.out.println("PUT操作");
        byteBuffer.put(s.getBytes());
        System.out.println(byteBuffer.position()); //5
        System.out.println(byteBuffer.limit());  //1024
        System.out.println(byteBuffer.capacity()); //1024
        System.out.println("***************************");

        System.out.println("filp操作");
        byteBuffer.flip();
        System.out.println(byteBuffer.position()); //0
        System.out.println(byteBuffer.limit());    //5
        System.out.println(byteBuffer.capacity()); //1024
        System.out.println("***************************");

        System.out.println("get操作");
        byte[] b=new byte[byteBuffer.limit()];
        byteBuffer.get(b,0,4);
        if(byteBuffer.hasRemaining()){ //判斷緩衝區是否有剩餘
            System.out.println("緩衝區還有幾個剩餘的: "+byteBuffer.remaining());
        }
        System.out.println(byteBuffer.position()); //5
        System.out.println(byteBuffer.limit());    //5
        System.out.println(byteBuffer.capacity()); //1024
        System.out.println("***************************");

        System.out.println("rwind操作可重複讀"); //又把position置於開頭
        byteBuffer.rewind();
        System.out.println(byteBuffer.position()); //0
        System.out.println(byteBuffer.limit());    //5
        System.out.println(byteBuffer.capacity()); //1024
        System.out.println("***************************");


        System.out.println("mark操作記錄當前的position位置"); //又把position置於開頭
        byteBuffer.mark();
        byte[] bytes=new byte[1024];
        byteBuffer.get(bytes,0,2); //取走前兩個,position爲2
        System.out.println("取走前兩個,position爲"+byteBuffer.position()); //2
        byteBuffer.reset(); //恢復到mark位置的position 爲0
        System.out.println("恢復到mark位置的position 爲"+byteBuffer.position()); //0
        System.out.println("***************************");

        System.out.println("clear操作清空(非真清空,相當於把position和limit恢復開始狀態)數組中的數據依然存在"); //又把position置於開頭
        byteBuffer.clear();
        System.out.println(byteBuffer.position()); //0
        System.out.println(byteBuffer.limit());    //1024
        System.out.println(byteBuffer.capacity()); //1024
        System.out.println("***************************");

        //獲取第一個bytez轉化爲char還是能輸出
        System.out.println("獲取第一個bytez轉化爲char還是能輸出"+(char)byteBuffer.get());

        if(byteBuffer.hasRemaining()){ //判斷緩衝區是否有剩餘
            System.out.println("緩衝區還有幾個剩餘的: "+byteBuffer.remaining());
        }

在這裏插入圖片描述

ByteBuffer byteBuffer=ByteBuffer.allocateDirect(1024);//創建一個直接緩衝區
System.out.println(byteBuffer.isDirect());//判斷是否爲直接緩衝區

三. 通道

在這裏插入圖片描述

通道:由java.nio.channels包定義。
Channel表示IO源與目標打開的連接。
Channel類似於傳統的“流”。但其自身不能直接訪問數據,Channel只能與Buffer進行交互。

java.nio.channels.Channel 接口

  • FileChannel:用於讀取、寫入、映射和操作文件的通道。
    
  • SocketChannel:通過 TCP 讀寫網絡中的數據。
    
  • ServerSocketChannel:可以監聽新進來的 TCP 連接,對每一個新進來的連接都會創建一個 SocketChannel。
    
  • DatagramChannel:通過 UDP 讀寫網絡中的數據通道。
    

3.1 java針對支持通道的類提供了getChannel()方法

  • 本地IO:
FileInputStream/FileOutputStream
long start=System.currentTimeMillis();

        FileInputStream fis=null;
        FileOutputStream fos=null;
        //獲取通道
        FileChannel inchannel = null;
        FileChannel outchannel = null;

        try {
            fis = new FileInputStream("E:\\image/1.jpg");
            fos = new FileOutputStream("E:\\image/3.jpg");
            inchannel = fis.getChannel();
            outchannel = fos.getChannel();
            //設置非直接緩衝區大小
            ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
            while(inchannel.read(byteBuffer)!=-1){ //將通道里的數據存入緩衝區
                byteBuffer.flip(); //切換讀取模式;
                outchannel.write(byteBuffer); // //將緩衝區中的數據寫入通道中
                byteBuffer.clear(); ////清空緩衝區
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally{
            if(outchannel!=null){
                try {
                    outchannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(inchannel!=null){
                try {
                    inchannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fos!=null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fis!=null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        long end=System.currentTimeMillis();
        System.out.println("耗費時間:"+(end-start));//耗費時間:1094
RandomAccessFile

在這裏插入圖片描述

      RandomAccessFile raf=new RandomAccessFile("E:\\image/1234.txt","rw");
        //建立通道
        FileChannel inChannel = raf.getChannel();
        //創建緩存區數組 ->分散讀取
        ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
        ByteBuffer byteBuffer1=ByteBuffer.allocate(100);
        ByteBuffer[] buffers={byteBuffer,byteBuffer1};
        //讀取到緩存區
        inChannel.read(buffers);
        for (ByteBuffer buffer : buffers) {
            buffer.flip(); //切換爲讀模式
        }
        //查看各自通道有多少
        System.out.println(new String(buffers[0].array(),0,buffers[0].limit()));
        System.out.println("****************************************************");
        System.out.println(new String(buffers[1].array(),0,buffers[1].limit()));

        //聚集寫入
        RandomAccessFile raf2=new RandomAccessFile("E:\\image/12345.txt","rw");
        FileChannel outChannel = raf2.getChannel();
        outChannel.write(buffers);
        inChannel.close();
        outChannel.close();
  • 網絡IO:
    Socket
    ServerSocket
    DatagramSocket

3.2 在JDK 1.7 中的NIO.2 針對各個通道提供了靜態方法 open()

   		long start=System.currentTimeMillis();
        //通過靜態方法 open()創建通道
        FileChannel inChannel=FileChannel.open(Paths.get("E:\\image/1.jpg"), StandardOpenOption.READ); //讀模式
        FileChannel outChannel=FileChannel.open(Paths.get("E:\\image/4.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE); //讀寫模式;存在就報錯,不存在就創建
        //內存映射文件
        MappedByteBuffer inMappedBuf=inChannel.map(FileChannel.MapMode.READ_ONLY,0,inChannel.size());
        MappedByteBuffer outMappedBuf=outChannel.map(FileChannel.MapMode.READ_WRITE,0,inChannel.size());
        //進行寫操作
        byte[] bytes=new byte[inMappedBuf.limit()];
        inMappedBuf.get(bytes);
        outMappedBuf.put(bytes);
        //關閉通道
        inChannel.close();
        outChannel.close();
        long end=System.currentTimeMillis();
        System.out.println("耗時: "+(end-start));

直接通過通道複製

 	    long start=System.currentTimeMillis();
        FileChannel inChannel=FileChannel.open(Paths.get("E:\\image/1.jpg"), StandardOpenOption.READ); //讀模式
        FileChannel outChannel=FileChannel.open(Paths.get("E:\\image/4.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE); //讀寫模式;存在就覆蓋,不存在就創建
        inChannel.transferTo(0,inChannel.size(),outChannel);
        inChannel.close();
        outChannel.close();
        long end=System.currentTimeMillis();
        System.out.println("耗時: "+(end-start));

3.3 在JDK 1.7 中的NIO.2 的Files工具類的newByteChannel()

3.4 測試結果

在這裏插入圖片描述

製作不易,轉載請標註~

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