• NIO之通道(Channel)的原理与获取以及数据传输与内存映射文件


    通道(Channel)

      由java.nio.channels包定义的,Channel表示IO源与目标打开的连接,Channel类似于传统的“流”,只不过Channel本身不能直接访问数据,Channel只能与Buffer进行交互。通道主要用于传输数据,从缓冲区的一侧传到另一侧的实体(如文件、套接字...),反之亦然;通道是访问IO服务的导管,通过通道,我们可以以最小的开销来访问操作系统的I/O服务;顺便说下,缓冲区是通道内部发送数据和接收数据的端点。

      在标准的IO当中,都是基于字节流/字符流进行操作的,而在NIO中则是是基于Channel和Buffer进行操作,其中的Channel的虽然模拟了流的概念,实则大不相同。

    区别StreamChannel
    支持异步 不支持 支持
    是否可双向传输数据 不能,只能单向 可以,既可以从通道读取数据,也可以向通道写入数据
    是否结合Buffer使用 必须结合Buffer使用
    性能 较低 较高

    传统与革新

    传统的数据流:
    CPU处理IO,性能损耗太大
    改为:
    内存和IO接口之间加了 DMA(直接存储器),DMA向CPU申请权限,IO的操作全部由DMA管理。CPU不要干预。
    若有大量的IO请求,会造成DMA的走线过多,则也会影响性能。
    则改DMA为Channel,Channel为完全独立的单元,不需要向CPU申请权限,专门用于IO。
     

    早一代IO操作是由CPU负责IO接口

    新一代DMA授权处理IO接口

    通道(Channel)模式

    Channel的实现类

    java.nio.channels.Channel 接口:
    |-- FileChannel
    |-- SocketChannel
    |-- ServerSocketChannel
    |-- DatagramChannel

    获取通道Channel

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

    本地IO

    • FileInputStream/FileOutputStream
    • RandomAccessFile

    网络IO

    • Socket
    • ServerSocket
    • DatagramSocket

    2.在jdk1.7中的NIO.2针对各个通道提供了静态方法open()

    FileChannel.open(Paths.get("d:\xxx.jpg"), StandardOpenOption.READ);

    3.在jdk1.7中的NIO.2的Files工具类的newByteChannel()

    Files.newByteChannel(filePath)

    通道之间的数据传输

    transferFrom()
    transferTo()

    StandardOpenOption介绍

    在打开文件通道时可以选择的选项有很多,其中最常见的是读取和写入模式的选择,分别通过java.nio.file.StandardOpenOption枚举类型中的READ和WRITE来声明。

    1. CREATE表示当目标文件不存在时,需要创建一个新文件;
    2. CREATE_NEW同样会创建新文件,区别在于如果文件已经存在,则会产生错误;
    3. APPEND表示对文件的写入操作总是发生在文件的末尾处,即在文件的末尾添加新内容;
    4. 当声明了TRUNCATE_EXISTING选项时,如果文件已经存在,那么它的内容将被清空;
    5. DELETE_ON_CLOSE用在需要创建临时文件的时候,声明了这个选项之后,当文件通道关闭时,Java虚拟机会尽力尝试去删除这个文件。

    代码示例

      1 package com.expgiga.NIO;
      2 
      3 import java.io.FileInputStream;
      4 import java.io.FileOutputStream;
      5 import java.io.IOException;
      6 import java.nio.ByteBuffer;
      7 import java.nio.MappedByteBuffer;
      8 import java.nio.channels.FileChannel;
      9 import java.nio.file.Paths;
     10 import java.nio.file.StandardOpenOption;
     11 
     12 /**
     13  * Channel:用于源节点与目标节点之间的连接。在Java NIO中,负责缓冲区中数据传输,Channel本身不存储数据,因此需要配合缓冲区进行传输。
     14  *
     40  *
     41  */
     42 public class TestChannel {
     43 
     44     public static void main(String[] args) throws IOException {
     45 
     46         /*
     47          * 1.利用通道完成文件的复制(非直接缓冲区)
     48          */
     49         FileInputStream fis = null;
     50         FileOutputStream fos = null;
     51 
     52         FileChannel inChannel = null;
     53         FileChannel outChannel = null;
     54 
     55         try {
     56             fis = new FileInputStream("1.jpg");
     57             fos = new FileOutputStream("2.jpg");
     58             //1.获取通道
     59             inChannel = fis.getChannel();
     60             outChannel = fos.getChannel();
     61 
     62             //2.分配指定大小的缓冲区
     63             ByteBuffer buffer = ByteBuffer.allocate(1024);
     64 
     65             //3.将通道中的数据缓冲区中
     66             while (inChannel.read(buffer) != -1) {
     67 
     68                 buffer.flip();//切换成都数据模式
     69 
     70                 //4.将缓冲区中的数据写入通道中
     71                 outChannel.write(buffer);
     72                 buffer.clear();//清空缓冲区
     73             }
     74         } catch (Exception e) {
     75             e.printStackTrace();
     76         } finally {
     77             if (outChannel != null) {
     78                 try {
     79                     outChannel.close();
     80                 } catch (IOException e) {
     81                     e.printStackTrace();
     82                 }
     83             }
     84 
     85             if (inChannel != null) {
     86                 try {
     87                     inChannel.close();
     88                 } catch (IOException e) {
     89                     e.printStackTrace();
     90                 }
     91             }
     92 
     93             if (fis != null) {
     94                 try {
     95                     fis.close();
     96                 } catch (IOException e) {
     97                     e.printStackTrace();
     98                 }
     99             }
    100 
    101             if (fos != null) {
    102                 try {
    103                     fos.close();
    104                 } catch (IOException e) {
    105                     e.printStackTrace();
    106                 }
    107             }
    108         }
    109 
    110 
    111         /*
    112          * 2.利用(直接缓冲区)通道完成文件的复制(内存映射文件的方式)
    113          */
    114       
    115         long start = System.currentTimeMillis();
    116         FileChannel inChannel2 = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
    117         FileChannel outChannel2 = FileChannel.open(Paths.get("3.jpg"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
    118 
    119         //内存映射文件
    120         MappedByteBuffer inMappedBuf = inChannel2.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
    121         MappedByteBuffer outMappedBuf = outChannel2.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
    122 
    123         //直接对缓冲区进行数据读写操作
    124         byte[] dst = new byte[inMappedBuf.limit()];
    125         inMappedBuf.get(dst);
    126         outMappedBuf.put(dst);
    127 
    128         inChannel2.close();
    129         outChannel2.close();
    130 
    131         long end = System.currentTimeMillis();
    132         System.out.println("耗费的时间为:" + (end - start));
    133 
    134         /*
    135          * 通道之间的数据传输(直接缓冲区)
    136          */
    137         FileChannel inChannel3 = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
    138         FileChannel outChannel3 = FileChannel.open(Paths.get("3.jpg"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
    139 
    140         inChannel3.transferTo(0, inChannel3.size(), outChannel3);
    141         //等价于
    142 //        outChannel3.transferFrom(inChannel3, 0, inChannel3.size());
    143 
    144         inChannel3.close();
    145         outChannel3.close();
    146     }
    147 }

     结果比较

    我复制的文件大小为312MB

    io time:2685
    nio channel time:1129
    nio buffer time:601

    说明nio操作缓冲区是最快的。

  • 相关阅读:
    JVMTIAgent
    Java 虚拟机编程接口JVMIT
    Java调试平台体系JPDA
    什么是缓存
    2019第49周日
    什么是你拥有的资本
    java里的static/final含义
    Java语言和JVM的使用说明书
    画图前端:mermaid。时序图/类图/甘特图/流程图/状态图/饼图。类似工具:Typora
    java通过下划线数字字面量增加可读性:10_00_00表示100000
  • 原文地址:https://www.cnblogs.com/shamo89/p/9612797.html
Copyright © 2020-2023  润新知