javaNIO實戰4----> java NIO的通道Channel實戰

該碼就碼發表於2020-10-22

1、IO發展三部曲

      在計算機的發展歷史中,IO的發展一共經歷了三個時期,分別如下:

      第一階段:CPU直接負責IO請求的處理,將檔案資料從磁碟讀取到記憶體中,然後再響應應用的IO請求,嚴重影響CPU的效能模型圖如下:

      第二階段:DMA(Direct Memory Access)直接儲存器 負責IO請求,不過DMA也會向CPU去獲取授權資訊,如果在頻繁進行IO操作的話,也會嚴重影響CPU的效能。模型圖如下:

       第三階段:使用Channel來替換DMA完成CPU對於IO請求的解放階段,Channel完全接管了IO請求,不需要CPU的參與,讓CPU全心全意做其他事情。模型如下:

 

2、NIO中Channel的概念以及作用

      在介紹NIO的時候我們談及到Channel(通道)的概念,其實他就是特殊的IO流,只不過它比傳統的IO流擁有更多的功能。在NIO中Channel主要就是用來連線輸入檔案與讀取點、連線輸出檔案與寫出點、緩衝區中資料的填充與清理、傳輸緩衝區中的資料。注意Channel本身不儲存資料,因此需要配合緩衝區進行傳輸,注意讀取點、寫入點一般情況下是我們的應用程式。

 

3、NIO中Channel的主要實現類

      

      FileChannel:主要用於本地檔案傳輸。

      SocketChannel、ServerSocketChannel:主要用於TCP網路IO傳輸。

      DatagramChannel:主要用於UDP網路IO傳輸。

 

4、如何建立通道Channel ?

      方式1:可以使用本地IO流例項通過getChannel()來獲取通道,如下IO流支援獲取Channel

                   FileInputStream、FileOutputStream、RandomAccessFile 案例如下:

        @Test
        public void test3() throws FileNotFoundException {
           FileInputStream fis = new FileInputStream("C://test.txt");
           FileChannel channel = fis.getChannel();

           FileOutputStream fos = new FileOutputStream("d://testCopy.txt");
           FileChannel channel1 = fos.getChannel();

           RandomAccessFile randomAccessFile = new RandomAccessFile("E://aaa.txt","E://aaa.txt");
           FileChannel channel2 = randomAccessFile.getChannel();
        }
    

                 上面的案例是針對本地IO流來獲取FileChannel的,網路IO也可以獲取到通道,案例如下:

        @Test
        public void test4() throws IOException {
           Socket socket = new Socket();
           SocketChannel channel1 = socket.getChannel();
        
           ServerSocket serverSocket = new ServerSocket(8080);
           ServerSocketChannel channe2 = serverSocket.getChannel();
        }

 

      方式2:在Java1.7 以後中NIO.2 針對各個通道的實現提供靜態方法open()。

                   在Java1.7以後更新的NIO的的部分統稱為NIO.2,案例如下:

        @Test
        public void test5() throws IOException {
           FileChannel fileChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ);
        }

                   網路中的IO通道也是可以使用open()方法直接獲取的。

      

      方式3:可以使用Java1.7中NIO.2的Files工具類的newByteChannel()獲取:

          @Test
          public void test7() throws IOException {
             SeekableByteChannel seekableByteChannel = Files.newByteChannel(Paths.get("2.jpg"), StandardOpenOption.READ);
          }

 

5、案例--->使用Channel進行檔案copy:

          實現1:使用檔案流獲取通道的方式:

           @Test
           public void test8() throws IOException {
               FileInputStream fileInputStream = new FileInputStream("2.jpg");
               FileOutputStream fileOutputStream = new FileOutputStream("3.jpg");
        
               FileChannel inChannel = fileInputStream.getChannel();
               FileChannel outChannel = fileOutputStream.getChannel();

               ByteBuffer buffer = ByteBuffer.allocate(1024);

               迴圈使用inChannel來讀取資料到緩衝區中,當沒有讀取到資料表示讀取結束
               while(inChannel.read(buffer) != -1){
                  讀完一個次,先將緩衝區切換為寫模式,然後使用outChannel將本次讀取的資料寫入到3.jpg中
                  buffer.flip();
                  outChannel.write(buffer);
                  寫完後再清理緩衝區。清理緩衝區後不需要再次切換為讀模式了,因為clear會重置position=0、limit=capacity
                  buffer.clear();
              }
          }

          實現2:使用靜態open()方法獲取通道,然後複製檔案:

             

            @Test
            public void test9() throws IOException {

                FileChannel inChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ);
                FileChannel outChannel = FileChannel.open(Paths.get("4.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
                //StandardOpenOption.CREATE:存在檔案就覆蓋,不存在就建立。
                //StandardOpenOption.CREATE_NEW:存在檔案就報錯,不存在就建立。

                ByteBuffer buffer = ByteBuffer.allocate(1024);

                迴圈使用inChannel來讀取資料到緩衝區中,當沒有讀取到資料表示讀取結束
                while(inChannel.read(buffer) != -1){
                    讀完一個次,先將緩衝區切換為寫模式,然後使用outChannel將本次讀取的資料寫入到3.jpg中  
                    buffer.flip();
                    outChannel.write(buffer);
                    寫完後再清理緩衝區。清理緩衝區後不需要再次切換為讀模式了,因為clear會重置position=0、limit=capacity
                    buffer.clear();
               }
            }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

相關文章