javaNIO实战4----> java NIO的通道Channel实战

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();
               }
            }

          实现3:使用内存映射缓冲区实现文件copy

          @Test
          public void test10() throws IOException {

             FileChannel inChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ);
             FileChannel outChannel = FileChannel.open(Paths.get("5.jpg"), 
                StandardOpenOption.WRITE,
                StandardOpenOption.READ,
                StandardOpenOption.CREATE);

             使用inChannel获取内存直接映射缓冲区,也是直接缓冲区。
             MappedByteBuffer inMapBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());

             使用outChannel获取内存直接映射缓冲区,也是直接缓冲区。
             MappedByteBuffer outMapBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());

             创建一个空字节数组,用于存放 从inMapBuffer获取数据。
             byte[] dest = new byte[inMapBuffer.limit()];

             获取inMapBuffer直接缓冲区中的数据。
             inMapBuffer.get(dest);

             将从inMapBuffer缓冲区中获取的数据put()到outMapBuffer直接缓冲区中。
             outMapBuffer.put(dest);
   
         }

 

          方式4:使用通道的transferTo、transferFrom来实现文件copy,即通道之间的数据传输:

            @Test
            public void test11() throws IOException {

              FileChannel inChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ);
              FileChannel outChannel = FileChannel.open(Paths.get("5.jpg"),
                  StandardOpenOption.WRITE,
                  StandardOpenOption.CREATE);

              transferTo、transferFrom的实现也是使用直接缓冲区的方式
              inChannel 传输到 outChannel
              inChannel.transferTo(0, inChannel.size(), outChannel);

              或者outChannel的数据从 inChannel 来
              //outChannel.transferFrom(outChannel, 0, inChannel.size());
           }

 

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页