深入浅出NIO之Channel、Buffer

举报
feichaiyu 发表于 2019/10/25 21:21:11 2019/10/25
【摘要】 本文转载自公众号 占小狼的博客前言Java NIO 由以下几个核心部分组成: 1 、Buffer2、Channel 3、Selector传统的IO操作面向数据流,意味着每次从流中读一个或多个字节,直至完成,数据没有被缓存在任何地方。NIO操作面向缓冲区,数据从Channel读取到Buffer缓冲区,随后在Buffer中处理数据。本文着重介绍Channel和Buffer的概念以及在文件读写方...

本文转载自公众号  占小狼的博客

前言

Java NIO 由以下几个核心部分组成: 

1 、Buffer

2、Channel 

3、Selector

传统的IO操作面向数据流,意味着每次从流中读一个或多个字节,直至完成,数据没有被缓存在任何地方。

NIO操作面向缓冲区,数据从Channel读取到Buffer缓冲区,随后在Buffer中处理数据。

本文着重介绍Channel和Buffer的概念以及在文件读写方面的应用和内部实现原理。

Buffer

A buffer is a linear, finite sequence of elements of a specific primitive type.

一块缓存区,内部使用字节数组存储数据,并维护几个特殊变量,实现数据的反复利用。 

1、mark:初始值为-1,用于备份当前的position; 

2、position:初始值为0,position表示当前可以写入或读取数据的位置,当写入或读取一个数据后,position向前移动到下一个位置; 

3、limit:写模式下,limit表示最多能往Buffer里写多少数据,等于capacity值;读模式下,limit表示最多可以读取多少数据。 

4、capacity:缓存数组大小

2.png

屏幕快照 2019-10-25 下午9.11.17.png屏幕快照 2019-10-25 下午9.11.59.png

ByteBuffer

A byte buffer,extend from Buffer

ByteBuffer的实现类包括"HeapByteBuffer"和"DirectByteBuffer"两种。

HeapByteBuffer

屏幕快照 2019-10-25 下午9.13.35.png

DirectByteBuffer通过unsafe.allocateMemory在物理内存中申请地址空间(非jvm堆内存),并在ByteBuffer的address变量中维护指向该内存的地址。 unsafe.setMemory(base, size, (byte) 0)方法把新申请的内存数据清零。

Channel

A channel represents an open connection to an entity such as a hardware device, a file, a network socket, or a program component that is capable of performing one or more distinct I/O operations, for example reading or writing.

NIO把它支持的I/O对象抽象为Channel,Channel又称“通道”,类似于原I/O中的流(Stream),但有所区别: 

1、流是单向的,通道是双向的,可读可写。 

2、流读写是阻塞的,通道可以异步读写。 

3、流中的数据可以选择性的先读到缓存中,通道的数据总是要先读到一个缓存中,或从缓存中写入,如下所示:

3.png

目前已知Channel的实现类有:

  • FileChannel

  • DatagramChannel

  • SocketChannel

  • ServerSocketChannel

FileChannel

A channel for reading, writing, mapping, and manipulating a file. 一个用来写、读、映射和操作文件的通道。

FileChannel的read、write和map通过其实现类FileChannelImpl实现。

read实现

屏幕快照 2019-10-25 下午9.15.12.png屏幕快照 2019-10-25 下午9.15.26.png

通过上述实现可以看出,基于channel的文件数据读取步骤如下: 

1、申请一块和缓存同大小的DirectByteBuffer bb。 

2、读取数据到缓存bb,底层由NativeDispatcher的read实现。 

3、把bb的数据读取到dst(用户定义的缓存,在jvm中分配内存)。 

read方法导致数据复制了两次

write实现

屏幕快照 2019-10-25 下午9.16.17.png屏幕快照 2019-10-25 下午9.16.31.png

通过上述实现可以看出,基于channel的文件数据写入步骤如下: 

1、申请一块DirectByteBuffer,bb大小为byteBuffer中的limit - position。 

2、复制byteBuffer中的数据到bb中。 

3、把数据从bb中写入到文件,底层由NativeDispatcher的write实现,具体如下:

屏幕快照 2019-10-25 下午9.17.22.png

write方法也导致了数据复制了两次

Channel和Buffer示例

屏幕快照 2019-10-25 下午9.17.35.png

注意buffer.flip() 的调用,首先将数据写入到buffer,然后变成读模式,再从buffer中读取数据。

总结

通过本文的介绍,希望大家对Channel和Buffer在文件读写方面的应用和内部实现有了一定了解,努力做到不被一叶障目。


喜欢本文的朋友们,欢迎长按下图关注订阅号程序员小灰,收看更多精彩内容

1.jpg

转载声明:本文转载自公众号【程序员小灰】

原文链接:https://mp.weixin.qq.com/s/rDvl9XhI93NXikvkrdYfFQ

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。