美文网首页
Java NIO——通道Channel

Java NIO——通道Channel

作者: 小波同学 | 来源:发表于2021-01-27 01:39 被阅读0次

Channel 通道的简介

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

应用程序与磁盘之间的数据写入或者读出,都需要由用户地址空间和内存地址空间之间来回复制数据,内存地址空间中的数据通过操作系统层面的IO接口,完成与磁盘的数据存取。在应用程序调用这些系统IO接口时,由CPU完成一系列调度、任务分配,早先这些IO接口都是由CPU独立负责。所以当发生大规模读写请求时,CPU的占用率很高。

之后,操作系统为了避免CPU完全被各种IO接口调用占用,引入了DMA(直接存储器存储)。当应用程序对操作系统发出一个读写请求时,会由DMA先向CPU申请权限,申请到权限之后,内存地址空间与磁盘之间的IO操作就全由DMA来负责操作。这样,在读写请求的过程中,CPU不需要再参与,CPU去做其他事情。当然,DMA来独立完成数据在磁盘与内存空间中的来去,需要借助于DMA总线。但是当DMA总线过多时,大量的IO操作也会造成总线冲突,即也会影响最终的读写性能。

为了避免DMA总线冲突对性能的影响,后来便有了通道的方式。通道,它是一个完全独立的处理器。CPU是中央处理器,通道本身也是一个处理器,专门负责IO操作。既然是处理器,通道有自己的IO命令,与CPU无关。它更适用于大型的IO操作,性能更高。

Java的NIO的通道类似流,但是又有一些不同:

  • 1、既可以从Channel中读数据也可以往Channel里面写数据;但是流的读写一般是单向的。
  • 2、Channel可以异步的读写。
  • 3、Channel的读写是通过Buffer这个中介实现的。数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。

下面是Java的NIO的最重要的几个channel的实现:

  • 1、FileChannel:主要是用于文件的读写。
  • 2、DatagramChannel:主要用于UDP读写网络中的数据。
  • 3、SocketChannel:通过TCP读写网络中的数据。
  • 4、ServerSocketChannel:主要用于服务端,可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。

FileChannel 类

FileChannel主要用来对本地文件进行 IO 操作,常见的方法有

  • public int read(ByteBuffer dst):从通道读取数据并放到缓冲区中。
  • public int write(ByteBuffer src):把缓冲区的数据写到通道中。
  • public long transferFrom(ReadableByteChannel src, long position, long count):从目标通道中复制数据到当前通道。
  • public long transferTo(long position, long count, WritableByteChannel target):把数据从当前通道复制给目标通道。

应用实例1——本地文件写数据

使用ByteBuffer(缓冲) 和 FileChannel(通道), 将 "hello,明天" 写入到file01.txt 中

public class NIOFileChannel01 {
    public static void main(String[] args) throws Exception{
        String str = "hello,明天";
        //创建一个输出流->channel
        FileOutputStream fileOutputStream = new FileOutputStream("d:\\file01.txt");

        //通过 fileOutputStream 获取 对应的 FileChannel
        //这个 fileChannel 真实 类型是  FileChannelImpl
        FileChannel fileChannel = fileOutputStream.getChannel();

        //创建一个缓冲区 ByteBuffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        //将 str 放入 byteBuffer
        byteBuffer.put(str.getBytes());

        //对byteBuffer 进行flip
        byteBuffer.flip();

        //将byteBuffer 数据写入到 fileChannel
        fileChannel.write(byteBuffer);
        fileOutputStream.close();
    }
}

应用实例2——本地文件读数据

使用ByteBuffer(缓冲) 和 FileChannel(通道), 将 file01.txt 中的数据读入到程序,并显示在控制台屏幕

public class NIOFileChannel02 {
    public static void main(String[] args) throws Exception {
        //创建文件的输入流
        File file = new File("d:\\file01.txt");
        FileInputStream fileInputStream = new FileInputStream(file);

        //通过fileInputStream 获取对应的FileChannel -> 实际类型  FileChannelImpl
        FileChannel fileChannel = fileInputStream.getChannel();

        //创建缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length());

        //将 通道的数据读入到Buffer
        fileChannel.read(byteBuffer);

        //将byteBuffer 的 字节数据 转成String
        System.out.println(new String(byteBuffer.array()));
        fileInputStream.close();
    }
}

应用实例3——使用一个Buffer完成文件读取

使用 FileChannel(通道) 和 方法 read , write,完成文件的拷贝

public class NIOFileChannel03 {
    public static void main(String[] args) throws Exception {
        FileInputStream fileInputStream = new FileInputStream("input.txt");
        FileChannel fileChannel01 = fileInputStream.getChannel();

        FileOutputStream fileOutputStream = new FileOutputStream("input2.txt");
        FileChannel fileChannel02 = fileOutputStream.getChannel();

        ByteBuffer byteBuffer = ByteBuffer.allocate(512);

        while (fileChannel01.read(byteBuffer) != -1) { //循环读取
            //将buffer中的数据写入到 fileChannel02 -- 2.txt
            byteBuffer.flip();
            fileChannel02.write(byteBuffer);
            byteBuffer.clear(); //清空buffer
        }
        //关闭相关的流
        fileInputStream.close();
        fileOutputStream.close();
    }
}

应用实例4——拷贝文件transferFrom 方法

使用 FileChannel(通道) 和 方法 transferFrom ,完成文件的拷贝

public class NIOFileChannel04 {
    public static void main(String[] args)  throws Exception {
        //创建相关流
        FileInputStream fileInputStream = new FileInputStream("d:\\a.jpg");
        FileOutputStream fileOutputStream = new FileOutputStream("d:\\a2.jpg");

        //获取各个流对应的filechannel
        FileChannel sourceCh = fileInputStream.getChannel();
        FileChannel destCh = fileOutputStream.getChannel();

        //使用transferForm完成拷贝
        destCh.transferFrom(sourceCh,0,sourceCh.size());
        //关闭相关通道和流
        sourceCh.close();
        destCh.close();
        fileInputStream.close();
        fileOutputStream.close();
    }
}

关于Buffer 和 Channel的注意事项和细节

  • 1、ByteBuffer 支持类型化的put 和 get,put 放入的是什么数据类型,get就应该使用相应的数据类型来取出,否则可能有 BufferUnderflowException 异常。

  • 2、可以将一个普通Buffer 转成只读Buffer。

  • 3、NIO 还提供了 MappedByteBuffer, 可以让文件直接在内存(堆外的内存)中进行修改, 而如何同步到文件由NIO 来完成。

  • 4、前面我们讲的读写操作,都是通过一个Buffer 完成的,NIO 还支持 通过多个Buffer (即 Buffer 数组) 完成读写操作,即 Scattering 和 Gathering。

MappedByteBuffer案例:

MappedByteBuffer修改文件

/**
 * MappedByteBuffer 可让文件直接在内存(堆外内存)修改,
 * 操作系统不需要在内核空间和用户空间来回的拷贝
 */
public class MappedByteBufferTest {

    public static void main(String[] args) throws Exception {
        RandomAccessFile randomAccessFile = new RandomAccessFile("input.txt", "rw");
        //获取对应的通道
        FileChannel channel = randomAccessFile.getChannel();

        /**
         * 参数1:FileChannel.MapMode.READ_WRITE 使用的读写模式
         * 参数2:0 可以直接修改的起始位置
         * 参数3: 5 是映射到内存的大小(不是索引位置) ,即将 input.txt 的多少个字节映射到内存
         * 可以直接修改的范围就是 0-5
         * 实际类型 DirectByteBuffer
         */
        MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
        mappedByteBuffer.put(0,(byte) 'H');
        mappedByteBuffer.put(3,(byte) '9');
        //mappedByteBuffer.put(5,(byte) 'Y');//IndexOutOfBoundsException

        randomAccessFile.close();
        System.out.println("修改成功");
    }
}

参考:
https://my.oschina.net/happyBKs/blog/1595866

相关文章

  • Java NIO Channel

    Channel(通道) 定义 Channel(通道)定义在java.nio.channels包中。Channel表...

  • Java NIO(4) - 通道(1)

    3.6 通道(channel)简介 通道(Channel):由 java.nio.channels 包定的。Cha...

  • Java NIO——通道Channel

    Channel 通道的简介 通道( Channel):由 java.nio.channels 包定义的。 Chan...

  • NIO 之 Channel

    可参考之前写过的文章:NIO 之 Channel实现原理 概述 通道( Channel)是 java.nio 的主...

  • Java NIO(2) - 缓冲区(Buffer)

    3. 缓冲区(Buffer)和通道(Channel) Java NIO系统的核心在于:通道(Channel)和缓冲...

  • NIO

    一、Java NIO 概述 二、Java NIO vs. IO 三、通道(Channel): 四、缓冲区(Buff...

  • Java NIO

    java Nio Selector 选择器Buffer 缓冲器Channel 通道 Selector是NIO的核心...

  • java NIO(一)Channel

    java NIO(一)Channel nio中的channel与流的区别在于,流的读写通常是单向的,而通道可以异步...

  • Java NIO 通道(Channel)

    Java NIO 通道(Channels )与流类似但也有一些区别: 您可以读取和写入通道,流通常是单向的(读或写...

  • [Java NIO]通道Channel

    一.通道介绍 1.1 通道(channel) 用于在字节缓冲区和位于通道的另一测的实体(通常是文件或套接字)之间有...

网友评论

      本文标题:Java NIO——通道Channel

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