通道(Channel)

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

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

区别 Stream Channel
支持异步 不支持 支持
是否可双向传输数据 不能,只能单向 可以,既可以从通道读取数据,也可以向通道写入数据
是否结合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虚拟机会尽力尝试去删除这个文件。

代码示例

 package com.expgiga.NIO;

 import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption; /**
* Channel:用于源节点与目标节点之间的连接。在Java NIO中,负责缓冲区中数据传输,Channel本身不存储数据,因此需要配合缓冲区进行传输。
*
*
*/
public class TestChannel { public static void main(String[] args) throws IOException { /*
* 1.利用通道完成文件的复制(非直接缓冲区)
*/
FileInputStream fis = null;
FileOutputStream fos = null; FileChannel inChannel = null;
FileChannel outChannel = null; try {
fis = new FileInputStream("1.jpg");
fos = new FileOutputStream("2.jpg");
//1.获取通道
inChannel = fis.getChannel();
outChannel = fos.getChannel(); //2.分配指定大小的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024); //3.将通道中的数据缓冲区中
while (inChannel.read(buffer) != -1) { buffer.flip();//切换成都数据模式 //4.将缓冲区中的数据写入通道中
outChannel.write(buffer);
buffer.clear();//清空缓冲区
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (outChannel != null) {
try {
outChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
} if (inChannel != null) {
try {
inChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
} if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
} if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} /*
* 2.利用(直接缓冲区)通道完成文件的复制(内存映射文件的方式)
*/
      
long start = System.currentTimeMillis();
FileChannel inChannel2 = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
FileChannel outChannel2 = FileChannel.open(Paths.get("3.jpg"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE); //内存映射文件
MappedByteBuffer inMappedBuf = inChannel2.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outMappedBuf = outChannel2.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size()); //直接对缓冲区进行数据读写操作
byte[] dst = new byte[inMappedBuf.limit()];
inMappedBuf.get(dst);
outMappedBuf.put(dst); inChannel2.close();
outChannel2.close(); long end = System.currentTimeMillis();
System.out.println("耗费的时间为:" + (end - start)); /*
* 通道之间的数据传输(直接缓冲区)
*/
FileChannel inChannel3 = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
FileChannel outChannel3 = FileChannel.open(Paths.get("3.jpg"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE); inChannel3.transferTo(0, inChannel3.size(), outChannel3);
//等价于
// outChannel3.transferFrom(inChannel3, 0, inChannel3.size()); inChannel3.close();
outChannel3.close();
}
}

结果比较

我复制的文件大小为312MB

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

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

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

  1. 通道(Channel)的原理与获取

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

  2. Java-NIO(五):通道(Channel)的数据传输与内存映射文件

    通道(Channel)的数据传输(采用非直接缓冲区) @Test public void testChannel() throws IOException { FileInputStream file ...

  3. Java-NIO(四):通道(Channel)的原理与获取

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

  4. JAVA NIO之浅谈内存映射文件原理与DirectMemory

    JAVA类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原 ...

  5. 【NIO】NIO之浅谈内存映射文件原理与DirectMemory

    Java类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原 ...

  6. Java NIO 内存映射文件

    Java NIO 内存映射文件 @author ixenos 文件操作的四大方法 前提:内存的访问速度比磁盘高几个数量级,但是基本的IO操作是直接调用native方法获得驱动和磁盘交互的,IO速度限制 ...

  7. Java NIO之内存映射文件——MappedByteBuffer

    大多数操作系统都可以利用虚拟内存实现将一个文件或者文件的一部分"映射"到内存中.然后,这个文件就可以当作是内存数组来访问,这比传统的文件要快得多. 内存映射文件的一个关键优势是操作 ...

  8. 【JavaNIO的深入研究4】内存映射文件I/O,大文件读写操作,Java nio之MappedByteBuffer,高效文件/内存映射

    内存映射文件能让你创建和修改那些因为太大而无法放入内存的文件.有了内存映射文件,你就可以认为文件已经全部读进了内存,然后把它当成一个非常大的数组来访问.这种解决办法能大大简化修改文件的代码.fileC ...

  9. 通道(Channel)的原理获取

    通道表示打开到 IO 设备(例如:文件.套接字)的连接.若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区.然后操作缓冲区,对数据进行处理.Channel 负责传输, ...

随机推荐

  1. NPOI读取Excel2003,2007

    using System; using System.Collections.Generic; using System.Data; using System.IO; using System.Lin ...

  2. 基于java过滤器实现的ajax跨域解决方案

    http://software.dzhuvinov.com/cors-filter-configuration.html

  3. ACM--输入三个字符(可以重复)后,按各字符的ASCII码从小到大的顺序输出这三个字符。

    代码如下: #include <stdio.h> main() { char a,b,c,d; int i; scanf("%d",&i); getchar() ...

  4. 再谈 Promise

    读完这篇文章,预计会消耗你 40 分钟的时间. Ajax 出现的时候,刮来了一阵异步之风,现在 Nodejs 火爆,又一阵异步狂风刮了过来.需求是越来越苛刻,用户对性能的要求也是越来越高,随之而来的是 ...

  5. ArcGIS中的多个栅格波段合成一幅影像

    此处用到了ArcGIS栅格处理中的Composite Bands工具( Data Management Tools --> Raster --> Raster Processing).具体 ...

  6. Matlab与C++混合编程 编写独立外部应用程序时出现“无法定位序数3906于动态链接库LIBEAY32.dll上”错误

    出现“无法定位序数3906于动态链接库LIBEAY32.dll上”错误,这种错误一般是同名函数出现在两个不同的头文件中了. 笔者的这个错误是由于 #include "mat.h" ...

  7. mormot数据库连接+查询+序列为JSON

    mormot数据库连接+查询+序列为JSON uses SynDB,SynCommons, SynDBRemote, SynOleDB, SynDBMidasVCL, mORMotMidasVCL p ...

  8. Docker时间和宿主同步

    通过date命令查看时间 查看主机时间 [root@localhost ~]# date 2016年 07月 27日 星期三 22:42:44 CST 查看容器时间 root@b43340ecf5ef ...

  9. springBoot框架的一些概念

    Spring的发展史1. Spring1.x 时代 在Spring1.x时代,都是通过xml文件配置bean,随着项目的不断扩大,需要将xml配置分放到不同的配置文件中,需要频繁的在java类和xml ...

  10. PostgreSQL配置文件--连接和认证

    1 连接和认证 CONNECTIONS AND AUTHENTICATION 1.1 连接 CONNECTIONS 1.1.1 listen_addresses 字符型 默认: listen_addr ...