查看“关于NIO”的源代码
←
关于NIO
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
[[category:Java]] == 关于 == Java NIO,即“'''New I/O'''”(另一说“'''No-Blocking I/O'''”):采用'''内存映射文件'''的方式来处理输入输出:NIO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样访问文件了。 *(相关内容:[http://tutorials.jenkov.com/java-nio/index.html?spm=a2c6h.12873639.0.0.3e1ba7fdKRM8ja Java NIO Tutorial],网上资料大多来自于此) === 与 IO === NIO与标准IO不同:'''NIO支持面向缓冲区的、基于通道的IO操作''',以更加高效的方式进行文件的读写操作。 *(“旧”的I/O包已经使用NIO重新实现过,“即使我们不显式的使用NIO编程,也能从中受益”) *(NIO子系统不会取代java.io包中可用的基于流的I/O类) {| class="wikitable" ! IO !! NIO |- | 面向流(Stream Oritented) | 面向缓冲区(Buffer Oritented) |- | 阻塞IO(Blocking IO) | 非阻塞IO(None Blocking IO) |- | 无 | 选择器(Selecters) |} === NIO 包 === 从JDK1.4开始提供的一系列改进的输入/输出处理,这些类都被放在java.nio包及子包下: :[[File:java.nio相关包.png|300px]] {| class="wikitable" ! 包名称 !! 使用/目的 |- | java.nio || NIO系统的顶级包,NIO系统封装了各种类型的缓冲区。 |- | java.nio.charset || 封装了字符集,并且还支持分别将字符转换为字节和字节到编码器和解码器的操作。 |- | java.nio.charset.spi || 用于支持字符集服务提供者 |- | java.nio.channels || 支持通道,这些通道本质上是打开I/O连接。 |- | java.nio.channels.spi || 用于支持频道的服务提供者 |- | java.nio.file || 提供对文件的支持 |- | java.nio.file.spi || 用于支持文件系统的服务提供者 |- | java.nio.file.attribute || 用于提供对文件属性的支持 |} ===(为什么学习NIO)=== IO操作往往在两个场景下会用到: # 文件I/O # 网络I/O:NIO的优势体现; ==【IO:同步、异步,阻塞、非阻塞】== (见[https://blog.csdn.net/historyasamirror/article/details/5778378 IO - 同步,异步,阻塞,非阻塞 (亡羊补牢篇)])<br/> == NIO 组件 == NIO中的所有I/O都是通过一个通道开始的。数据总是从缓冲区写入通道,并从通道读取到缓冲区: # 从通道读取:创建一个缓冲区,然后请求通道读取数据; #: [[File:NIO:数据读取.png|400px]] # 通道写入:创建一个缓冲区,填充数据,并要求通道写入数据; #: [[File:NIO:数据写入.png|400px]] Java NIO 由以下几个核心部分组成: * “'''Buffer'''” * “'''Channel'''” * “'''Selector'''” === Channel === 通道,是用于在实体和字节缓冲区之间有效传输数据的介质。它从一个实体读取数据,并将其放在缓冲区块中以供消费。 Java NIO Channel 不同于流: # 既可以从通道中读取数据,又可以写数据到通道;但流的读写通常是单向的。 # 通道可以异步地读写。 # 通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。 “java.nio.channels”类的层次结构: : [[File:java.nio.channels类的层次结构.png|400px]] 其中顶层接口: <syntaxhighlight lang="java"> package java.nio.channels; import java.io.IOException; import java.io.Closeable; public interface Channel extends Closeable { // 检查通道是否打开 public boolean isOpen(); // 关闭通道 public void close() throws IOException; } </syntaxhighlight> Java NIO中,主要使用的通道如下: # “'''FileChannel'''”:从文件中读写数据; # “'''DatagramChannel'''”:通过UDP读写网络中的数据; # “'''SocketChannel'''”:通过TCP读写网络中的数据; # “'''ServerSocketChannel'''”:监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel; 示例: <syntaxhighlight lang="java"> RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw"); FileChannel inChannel = aFile.getChannel(); ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf); while (bytesRead != -1) { System.out.println("Read " + bytesRead); buf.flip(); while(buf.hasRemaining()) { System.out.print((char) buf.get()); } buf.clear(); bytesRead = inChannel.read(buf); } aFile.close(); </syntaxhighlight> === Buffer === Java NIO中的Buffer用于和NIO通道进行交互: : 缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。 :[[File:java.nio.Buffer类结构.png|300px]] 在Java NIO中使用的核心缓冲区如下: * '''ByteBuffer''' * MappedByteBuffer * CharBuffer * DoubleBuffer * FloatBuffer * IntBuffer * LongBuffer * ShortBuffer ==== capacity、position和limit ==== 缓冲区本质上是一块“可以写入数据,然后可以从中读取数据”的内存:这块内存被包装成“NIO Buffer”对象,并提供了一组方法,用来方便的访问该块内存。<br/> 为了理解Buffer的工作原理,需要熟悉它的三个属性: # '''capacity''':容量; # '''position''':读写位置,即下一个值将在此进行读写; ## 写模式:初始的position值为0,最大可为capacity-1; ## 读模式:当将Buffer从写模式切换到读模式,position会被重置为0; # '''limit''':界限,即超过它进行读写是没有意义的; ## 写模式:limit等于Buffer的capacity。 ## 读模式:当切换Buffer到读模式时,limit会被设置成写模式下的position值(已写入数据位置); * 不管Buffer处在什么模式,“capacity”的含义总是一样的; : [[File:capacity、position和limit.png|400px]] ==== 基本用法 ==== 使用Buffer读写数据,遵循以下四个步骤: # 写入数据到Buffer; // 向buffer写入数据时,buffer会记录下写了多少数据 # 调用“'''flip()'''”方法; // 通过flip()方法将Buffer从写模式切换到读模式 # 从Buffer中读取数据; // 读模式下,可以读取之前写入到buffer的所有数据 # 调用“'''clear()'''”方法或者“'''compact()'''”方法; // 读完所有数据,就清空缓冲区让它可以再次被写入:clear()方法会清空整个缓冲区;compact()方法只会清除已经读过的数据 <syntaxhighlight lang="java"> RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw"); FileChannel inChannel = aFile.getChannel(); //create buffer with capacity of 48 bytes ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf); //read into buffer. while (bytesRead != -1) { buf.flip(); //make buffer ready for read while(buf.hasRemaining()) { System.out.print((char) buf.get()); // read 1 byte at a time } buf.clear(); //make buffer ready for writing bytesRead = inChannel.read(buf); } aFile.close(); </syntaxhighlight> # Buffer的分配: #: <syntaxhighlight lang="java"> // 分配48字节capacity的ByteBuffer ByteBuffer buf = ByteBuffer.allocate(48); // 分配一个可存储1024个字符的CharBuffer CharBuffer buf = CharBuffer.allocate(1024); </syntaxhighlight> # 向Buffer中写数据: ## 从Channel写到Buffer: ##: <syntaxhighlight lang="java"> int bytesRead = inChannel.read(buf); //read into buffer. </syntaxhighlight> ## 通过Buffer的“put()”方法写到Buffer里: ##: <syntaxhighlight lang="java"> buf.put(127); </syntaxhighlight> ##* “'''put()'''”方法有很多版本,允许以不同的方式把数据写入到Buffer中; # 从Buffer中读取数据: ## 从Buffer读取数据到Channel: ##: <syntaxhighlight lang="java"> int bytesWritten = inChannel.write(buf); // read from buffer into channel. </syntaxhighlight> ## 使用“get()”方法从Buffer中读取数据: ##: <syntaxhighlight lang="java"> byte aByte = buf.get(); </syntaxhighlight> ##* “get”方法有很多版本,允许以不同的方式从Buffer中读取数据; ==== 常用方法 ==== * “'''flip()'''”:准备读入: *: 即,将“position”设回0,并将“limit”设置成之前“position”的值。 * “'''rewind()'''”:准备重新读入: *: 即,将“position”设回0,所以你可以重读Buffer中的所有数据。“limit”保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)。 * “'''clear()'''”与“'''compact()'''”:准备写入: *# “clear()”:“position”将被设回0,“limit”被设置成 capacity的值。(Buffer被清空了,但Buffer中的数据并未清除,只是这些标记告诉我们可以从哪里开始往Buffer里写数据) *#* '''Buffer中未读数据将“被遗忘”。''' *# “compact()”:将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。limit属性依然像clear()方法一样,设置成capacity。 *#* '''不会覆盖未读的数据。''' * “'''mark()'''”与“'''reset()'''”: *: 通过调用Buffer.mark()方法,可以标记Buffer中的一个特定position。之后可以通过调用Buffer.reset()方法恢复到这个position。 * “'''equals()'''”与“'''compareTo()'''”:比较两个Buffer:(剩余元素是从 position到limit之间的元素) *# “equals()”:当满足下列条件时,表示两个Buffer相等:(只是比较Buffer的一部分,不是每一个在它里面的元素都比较。实际上,它只比较Buffer中的剩余元素) *## 有相同的类型(byte、char、int等)。 *## Buffer中剩余的byte、char等的个数相等。 *## Buffer中所有剩余的byte、char等都相同。 *# “compareTo()”:比较两个Buffer的剩余元素(byte、char等), 如果满足下列条件,则认为一个Buffer“小于”另一个Buffer: *## 第一个不相等的元素小于另一个Buffer中对应的元素。 *## 所有元素都相等,但第一个Buffer比另一个先耗尽(第一个Buffer的元素个数比另一个少)。 === Selector === == Scatter / Gather == “scatter/gather”:用于描述从Channel中读取或者写入到Channel的操作: # 分散(scatter)读取:将从Channel中读取的数据“分散(scatter)”到多个Buffer中; #: [[File:Java NIO:Scattering Read.png|300px]] #: <syntaxhighlight lang="java"> ByteBuffer header = ByteBuffer.allocate(128); ByteBuffer body = ByteBuffer.allocate(1024); ByteBuffer[] bufferArray = { header, body }; channel.read(bufferArray); </syntaxhighlight> #: read()方法按照buffer在数组中的顺序将从channel中读取的数据写入到buffer,当一个buffer被写满后,channel紧接着向另一个buffer中写。 #* '''Scattering Reads在移动下一个buffer前,必须填满当前的buffer''',这也意味着它不适用于动态消息(消息大小不固定)。 # 聚集(gather)写入:将多个Buffer中的数据“聚集(gather)”后发送到Channel; #: [[File:Java NIO:Gathering Write.png|300px]] #: <syntaxhighlight lang="java"> ByteBuffer header = ByteBuffer.allocate(128); ByteBuffer body = ByteBuffer.allocate(1024); //write data into buffers ByteBuffer[] bufferArray = { header, body }; channel.write(bufferArray); </syntaxhighlight> #: write()方法会按照buffer在数组中的顺序,将数据写入到channel,注意只有position和limit之间的数据才会被写入。 #: 与Scattering Reads相反,Gathering Writes能较好的处理动态消息。 == Channel to Channel Transfers == == 常用Channel == FileChannel DatagramChannel SocketChannel ServerSocketChannel == Java NIO非阻塞式服务器 == == 其他 == Pipe Path Files AsynchronousFileChannel
返回至“
关于NIO
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
大陆简体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
笔记
服务器
数据库
后端
前端
工具
《To do list》
日常
阅读
电影
摄影
其他
Software
Windows
WIKIOE
所有分类
所有页面
侧边栏
站点日志
工具
链入页面
相关更改
特殊页面
页面信息