NIO (一)缓冲区Buffer、通道Channel 学习记录,以便温习。

通道有缓存区就像铁路与火车,铁路本身不具备运输能力,需要依靠火车的运作才具备运输能力,同样的,通道本身不具备数据传输能力,需要与缓冲区的配合,才能完成数据的传输。


通道有FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel这几类。

获取通道的方式:

1.通过getChannel()方法获取。

本地IO:FileOutPutStream,FileOutPutStream,RandomAccessFile 

网络IO:Socket,ServerSocket  ,DatagramSocket

FileChannel insChannel = ins.getChannel();

2.在JDK1.7以后,nio中针对各个IO提供静态方法open。

fileChannel = FileChannel.open(Path,StandardOpenOption.READ等)

3.JDK1.7以后,nio中的Files工具类提供newByteChannel方法。

缓冲区分为:直接缓冲区,非直接缓冲区

直接缓冲区创建在JVM上,通过allocate(int size)方法创建;

非直接缓冲区创建在物理机的内存中,通过allocateDirect(int size)创建;

直接缓冲区的性能比非直接缓冲区的性能要高,因为非直接缓冲区在应用程序与磁盘之间有个拷贝的过程,而直接缓冲区是通过内存文件映射的方式进行直接交互,没有拷贝过程,所以性能较好,但是会占据系统的内存。

下面的代码是使用缓冲区与通道的具体例子(均以文件复制为例):

1.非直接缓冲区

         

 FileInputStream ins = new FileInputStream(filePath);
			String newPath = filePath.substring(0,filePath.lastIndexOf(".")) + 
					"COPY" + filePath.substring(filePath.lastIndexOf("."));
			
			FileOutputStream out = new FileOutputStream(newPath);
			
			//1.获取通道
			FileChannel insChannel = ins.getChannel();
			FileChannel outChannel = out.getChannel();
			
			//2.分配缓冲区ByteBuffer
			ByteBuffer buffer = ByteBuffer.allocate(1024);
			
			//读取缓冲区数据
			while(insChannel.read(buffer) != -1){
				buffer.flip();//转换到写入模式。
				outChannel.write(buffer);//将缓冲区写入到通道
				buffer.clear();//清空缓冲区
			}
			
			System.out.println("copy完成...copy文件:" + newPath);
			//关闭通道
			insChannel.close();
			outChannel.close();
			ins.close();
			out.close();

读取缓冲区数据时,如果read返回的-1则表示读到缓冲区末尾了。获取通道中的缓冲区后,通过flip()方法将写入模式转换为读取模式,flip方法源码如下,调用flip方法时,将limit(最多可读)设置为当前的option(缓冲区中当前写入的位置)的值(缓冲区写入模式中limit最大不超过capacity),并将option设置为0。

    public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

2.直接缓冲区

                       // 获取通道,StandardOpenOption 枚举类,具体属性查看源码。
			FileChannel inChannel = FileChannel.open(Paths.get(filePath), StandardOpenOption.READ);
			String newPath = filePath.substring(0, filePath.lastIndexOf(".")) + "COPY"
					+ filePath.substring(filePath.lastIndexOf("."));
			// 给写入通道添加读权限、 写入权限和创建文件权限。
			FileChannel outChannel = FileChannel.open(Paths.get(newPath), StandardOpenOption.WRITE,
					StandardOpenOption.READ, StandardOpenOption.CREATE);

			// 内存映射文件,只有ByteBuffer才有直接缓冲区操作。
			// 内存映射文件只读模式。
			MappedByteBuffer inMapBuffer = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());

			// 内存映射读写模式。
			MappedByteBuffer outMapBuffer = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());

			// 直接对缓冲区进行数据读写操作 limit 属性表示当前缓冲区最多可读多少。
			byte[] dst = new byte[inMapBuffer.limit()];
			inMapBuffer.get(dst);
			outMapBuffer.put(dst);

			System.out.println("copy完成...copy文件:" + newPath);
			inChannel.close();
			outChannel.close();

1.创建通道fileChannel = FileChannel.open(Path,StandardOpenOption.READ等)
2.创建完通道后,通过inChannel.map(MapMode.READ_ONLY, 0, inChannel.size())返回的是 MappedByteBuffer 实际上就是内存映射文件

3.获得映射对象后,通过put,get等方法将byte数组写入到内存映射中去。inMapBuffer.get(byte[]);outMapBuffer.put(byte[]);等方法。

练习的时候有个小插曲,如果outChannel中没有设置read属性即StandardOpenOption.READ,而在创建MappedByteBuffer时MapMode选择的是READ_WRIT,运行会报异常。

Exception in thread "main" java.nio.channels.NonReadableChannelException
	at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:840)
	at com.channel.TestChannel.copyFile2(TestChannel.java:35)
	at com.channel.TestChannel.main(TestChannel.java:258)

还有注意StandardOpenOption中CREATE和CREATE_NEW,两则都表示当文件不存在时创建一个文件,两个的区别是:CREATE_NEW表示当文件存在时,则报错,抛出异常。

3.通道间的数据传输

//获取通道,StandardOpenOption 枚举类,具体属性查看源码。
			FileChannel inChannel = FileChannel.open(Paths.get(filePath), StandardOpenOption.READ);
			String newPath = filePath.substring(0,filePath.lastIndexOf(".")) + 
					"COPY" + filePath.substring(filePath.lastIndexOf("."));
			//给写入通道添加读权限、 写入权限和创建文件权限。
			FileChannel outChannel = FileChannel.open(Paths.get(newPath), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
			
			inChannel.transferTo(0, inChannel.size(), outChannel);
//			与前面等价 
//			writeChannel.transferFrom(readerChannel, 0, readerChannel.size());
			System.out.println("copy完成...copy文件:" + newPath);
			inChannel.close();
			outChannel.close();
			


通道间的数据传输本质也是直接缓冲区,可以通过transferTo或则transfreFrom方法,直接在两个通道间数据。

以上三种文件复制的方法,通过测试,通道间数据传输<直接缓冲区<普通IO<非直接缓冲区。都是复制一个500M大小的文件,测试结果跟本机也有关系。通道传输的的内存消耗比直接缓冲区的略少,普通IOCPU消耗较高,非直接缓冲区各项均衡。

分散读取与聚集写入:
分散读取: 将通道的数据按顺序分散到各个缓冲区
聚集写入: 将多个缓冲区的数据聚集到一个通道中。
FileChannel.reade(ByteByffer[])
FileChannel.write(ByteBuffer[])
原理就是创建缓冲区数组,由原来的操作单个缓冲区变为操作缓冲区数组。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值