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操作缓冲区是最快的。

原文地址:https://www.cnblogs.com/shamo89/p/9612797.html