天天看点

Java NIO

java是非常繁杂的语言, 比如io就是典型的代表...

首先1.0, 是基于8位字节流的inputstream和outputstream系列 

然后是1.1, 是基于16位的字符流(unicode)的reader和writer系列

下表是对应关系, 其中inputstreamreader和outputstreamwriter, 起到两个系列之间的适配作用

当然实际的继承关系比这个复杂的多, 通过装饰模式, 产生各种io类, 非常繁杂

Java NIO

从1.4开始, java提供new io 

其实在new io中主要两个提升 

a, buffer和channel 

解决小块数据传输带来的效率问题, 引入buffer数据结构, 可以批量传输以提高效率(这样更符合底层数据传输的方式), 一个挖煤的比喻, 从挖一铲运一铲到挖满一卡车再运出来

b, 对于socket channel加入非阻塞方式

提供一种支持丰富操作的数据结构, 同时虽然对于所有的类型(除bool类型)都有相应的buffer, 但是只有bytebuffer可以直接被channel读取, 其余的都需要在放到channel之前做类型转换

Java NIO

数据结构

Java NIO

支持操作

Java NIO

下图以filechannel为例子, 

首先channel只能接收bytebuffer作为write/read的参数 

对于其他的buffer必须做类型转换, 尤其对于charbuffer需要考虑charset的encode/decode

Java NIO

管道很形象, 就是连接发送端和接收端之间的媒介 

其实就是对于传统socket或file接口针对bytebuffer的封装

i/o可以分为广义的两大类别:file i/o和stream i/o 

所以对应的, channel分为两类, filechannel和socket相关channel(socketchannel、serversocketchannel和 datagramchannel)

filechannel

filechannel类可以实现常用的read,write以及scatter/gather操作(对于多个buffer的批处理), 同时它也提供了很多专用于文件的新方法. 

文件通道总是阻塞式的, 因为现代操作系统都有复杂的缓存和预取机制, 所以本地磁盘i/o操作延迟很少

filechannel对象不能直接创建。一个filechannel实例只能通过在一个打开的file对象(randomaccessfile、fileinputstream或 fileoutputstream)上调用getchannel( )方法获取

filechannel对象是线程安全(thread-safe)

1.4中, java通过filechannel实现了文件锁(之前java不支持文件锁), 但是这是进程级别锁, 相同进程中不同线程无法通过文件锁进行互斥

socketchannel

新的socket通道类可以运行非阻塞模式, 这个大大提升了java io的性能, 之前只能使用多线程阻塞的方式来处理并发, 但线程调度的开销也很高尤其当维护大量线程的时候

全部socket通道类(datagramchannel、socketchannel和serversocketchannel), 分别对应于java.net中的(socket、serversocket和datagramsocket), 并且channel其实就是对他们的封装

serversocketchannel

从上面两个例子可以看出channel的使用, 其实和原来的api没有很大的不同, 关键就是支持non-blocking方式

当然还需要selector, 不然非阻塞意义不大, 象c/c++中的select, poll

只有继承selectablechannel的channel类才可以被注册到selector对象上, 所以filechannel对象不是可选择的, 而所有socketchannel都是可选择的

selectionkey

代表了selector和selectablechannel的注册关系 

key.attachment(); //返回selectionkey的attachment,attachment可以在注册channel的时候指定 

key.channel(); // 返回该selectionkey对应的channel 

key.selector(); // 返回该selectionkey对应的selector 

key.interestops(); //返回代表需要selector监控的io操作的bit mask 

key.readyops(); //返回一个bit mask,代表在相应channel上可以进行的io操作

使用的代码