Java NIO 三大组件之 Channel

2019-12-03 来源: ilmostro 发布在  https://www.cnblogs.com/mostro/p/11977267.html

Java NIO 之 Channel

一、什么是Channel

Channel用于源节点(例如磁盘)与目的节点的连接,它可以进行读取,写入,映射和读/写文件等操作。

在Java NIO中负责缓冲区中数据的传输。Channel本省不存储数据,因此需要配合缓冲区进行传输。(个人理解其实就是相当于保存两通信地间的上写问和进行读写操作,相当于邮局)

二、通道的主要实现类

java.nio.channels.Channel接口:

  • FileChannel(本地)
  • SocketChannel(TCP)
  • ServerSocketChannel(TCP)
  • DatagramChannel(UDP)

三、获取通道的方式

  1. Java 针对支持通道的类提供了getChannel()方法

    • FileInputStream/FileOutputStream(本地IO)
    • RandomAccessFile(本地IO)
    • Socket(网络IO)
    • ServerSocket(网络IO)
    • DatagramSocket(网络IO)
  2. 在JDK1.7中NIO.2针对各个通道提供了静态方法open()
  3. 在JDK1.7中NIO.2的Files工具类的newByteChannel()

四、通道之间的数据传输

  • transferFrom()
  • transferTo()
@Test
public void test3() throws IOException {
    FileChannel inChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ);
    FileChannel outchannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);

    inChannel.transferTo(0,inChannel.size(),outchannel);
    //outchannel.transferFrom(inChannel, 0,inChannel.size());
    inChannel.close();
    outchannel.close();
}

其他两种两种传输方式

  1. 使用直接缓冲区完成文件的复制(内存映射文件)
//实验证明如果传大型文件内存会出现大量占用,如果GC不对内存进行回收,无法确定OS何时把数据写入磁盘
@Test
public void test2() throws IOException {
    FileChannel inChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ);
    FileChannel outchannel = FileChannel.open(Paths.get("1.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[] dst = new byte[inMappedBuf.limit()];
    inMappedBuf.get(dst);
    outMappedBuf.put(dst);

}
  1. 利用通道完成文件的复制(非直接缓冲区)
@Test
public void test1(){
    String path = this.getClass().getClassLoader().getResource("1.jpg").getPath();

    FileInputStream fis = null;
    FileOutputStream fos = null;

    FileChannel fisChannel = null;
    FileChannel fosChannel = null;
    try {
         fis = new FileInputStream(path);
         fos = new FileOutputStream("src/main/resources/2.jpg");

        //获取通道
         fisChannel = fis.getChannel();
         fosChannel = fos.getChannel();

        //二、分配指定大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);

        //三、将通道中的数据存入缓冲区中
        while(fisChannel.read(buf) != -1){
            buf.flip(); //切换读取数据的模式
            //四、将缓冲区的数据写入通道中
            fosChannel.write(buf);
            buf.clear();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        try {
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

分散(Scatter)与聚集(Gather)

  • 分散读取(Scattering Reads): 将通道中的数据分散到多个缓冲区中
  • 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中

相关文章