Java NIO Channel

作者: Chermack | 来源:发表于2020-06-08 17:06 被阅读0次

Channel(通道)

定义

Channel(通道)定义在java.nio.channels包中。Channel表示IO源与目标打开的连接。Channel类似于传统的“流”,但Channel本身不能直接访问数据,只能与Buffer进行交互。

由来

早期的IO由CPU负责,由于CPU与物理磁盘之间存在巨大的运算与传输速率差距,导致CPU利用率不高。于是有了DMA来单独处理IO部分。但是当应用程序有大量的IO请求时,又会造成总线冲突的问题,于是将DMA更新成了通道的方式。通道与DMA的区别在于,当有IO操作时,DMA需要向CPU请求获取总线的控制权,进行IO操作,然后将总线的控制权归还CPU;通道则是一个完全独立的处理器,无需向CPU申请总线的控制权,专门用于IO操作。

通道方式的IO

Channel接口的主要实现类

  • FileChannel :用于读取、写入、映射和操作文件的通道。
  • DatagramChannel:通过UDP读写网络中的数据通道。
  • SocketChannel:通过TCP读写网络中的数据。
  • ServerSocketChannel:可以监听新进来的TCP连接,对每一个新进来的连接都会创建一个SocketChannel。

获取通道

  1. Java针对支持通道的类提供了getChannel()方法:
    本地IO:
  • FileInputStream/FileOutputStream
  • RandomAccessFile
    网络IO:
  • Socket
  • ServerSocket
  • DatagramSocket

利用通道完成文件的复制的代码如下:

    @Test
    public void test1() throws IOException {
        FileInputStream fis =new FileInputStream("1.png");
        FileOutputStream fos = new FileOutputStream("2.png");
        //①获取通道
        FileChannel inChannel = fis.getChannel();
        FileChannel outChannel = fos.getChannel();
        
        //②分配指定大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);

        //③将通道中的数据存入缓冲区中
        while (inChannel.read(buf) != -1) {
            buf.flip();//切换到读模式
            //⑤将缓冲区中的数据写入通道中
            outChannel.write(buf);
            buf.clear();//清空缓冲区
        }

        //关闭连接,释放资源
        inChannel.close();
        outChannel.close();
        fis.close();
        fos.close();
    }

2.在JDK1.7中的NIO.2针对各个通道提供了静态方法open()
使用直接缓冲区完成文件的复制(内存映射文件)代码如下:

    @Test
    public void test2() throws IOException {
        FileChannel inChannel = FileChannel.open(Paths.get("1.png"), 
                StandardOpenOption.READ); //打开源文件读通道
        FileChannel outChannel = FileChannel.open(Paths.get("3.png"), 
                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[] dst = new byte[inMappedBuf.limit()];
        inMappedBuf.get(dst);
        outMappedBuf.put(dst);
        inChannel.close();
        outChannel.close();
    }

也可直接使用transferFrom或transferTo通过直接缓冲区(不需要在用户代码中声明)直接传递,并且效率更高。但这样也会增加系统的开销,只有借助垃圾回收机制才能对直接缓冲区资源进行回收。

    @Test
    public void test3() throws IOException {
        FileChannel inChannel = FileChannel.open(Paths.get("1.png"),
                StandardOpenOption.READ); //打开源文件读通道
        FileChannel outChannel = FileChannel.open(Paths.get("4.png"),
                StandardOpenOption.WRITE,
                StandardOpenOption.READ,
                StandardOpenOption.CREATE);//打开一个目标文件通道,如果没有则创建,否则覆盖,并赋予读写权限
        //两个通道之间直接传输数据进行回收
//        inChannel.transferTo(0, inChannel.size(), outChannel);
        outChannel.transferFrom(inChannel, 0, inChannel.size());
        inChannel.close();
        outChannel.close();
    }

分散(Scatter)与聚集(Gather)

  • 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中。(按照缓冲区顺序,从Channel中读取的数据一次将Buffer填满)。


    分散读取
  • 聚集写入(Gathering Writes):将多个缓冲区中的数据按照顺去聚集写入到通道中。


    聚集写入

示例代码如下:

    @Test
    public void test4() throws IOException {
        RandomAccessFile raf1 = new RandomAccessFile("1.txt", "rw");
        //1.获取通道
        FileChannel inChannel = raf1.getChannel();

        //2.分配指定大小的缓冲区
        ByteBuffer buf1 = ByteBuffer.allocate(100);
        ByteBuffer buf2 = ByteBuffer.allocate(1024);

        //3.分散读取
        ByteBuffer[] bufs = {buf1, buf2};
        inChannel.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 outChannel = raf2.getChannel();
        outChannel.write(bufs);
    }

相关文章

网友评论

    本文标题:Java NIO Channel

    本文链接:https://www.haomeiwen.com/subject/zjtbzhtx.html