Java NIO之Java中的IO分类
前言
前面两篇文章(Java NIO之理解I/O模型(一)、Java NIO之理解I/O模型(二))介绍了,IO的机制,以及几种IO模型的内容,还有涉及到的设计模式。这次要写一些更贴近实际一些的内容了,终于要说到了Java中的各种IO了。我也是边学边理解,有写的不对的地方,欢迎小伙伴们指出和补充。
Java中的IO分类
BIO
BIO是指 Blocking IO 在JDK1.0的时候就引入了,直到JDK1.4一直都是Java中唯一的IO方式。它的主要实现方式就是,一个线程执行一个请求,如果请求数据量较大线程就会一直占用着,又或者请求什么也不做,也是会占用一个线程的,这样当客户端请求数量变多时,服务端线程数也跟着变多,最终就会导致服务端CPU崩溃的。当然可以使用线程池来减小CPU的压力,但是毕竟线程池也不是从本质上解决问题(长链接,线程池大小,以及拒绝策略等等因素都要考虑)。
无论是网络连接还是本地读取输出操作都是这种方式。具体的例子我就不举了(毕竟BIO不是本次的重点)。
NIO
Java中的NIO其实就是使用的多路I/O复用模型,前面的文章已经介绍过原理了,但是在理解Java的NIO之前,还是先介绍几个Java NIO的基础概念:Channel(通道),Buffer(缓冲区),Selector(选择器)。
Channel(通道)
Channel可以理解为,互通的管道,和Java的IO中的各种Stream(InputStream、OutputStream等等)一个等级,只不过Channel是双向的,而Stream是单向的。通道的作用是将数据移入或移出道各种I/O源,即可读又可写。
在Java中Channel类的层次结构相当复杂,有多个接口和许多可选操作。不过,常用的也就几个。
FileChannel
DatagramChannel
SocketChannel
ServerSocketChannel
FileChannel可以对文件进行读和写,DatagramChannel可以以UDP的协议来进行数据读写,SocketChannel以TCP的协议来对网络两端进行读写,ServerSocketChanel能够监听客户端发起的TCP连接,并为每个TCP连接创建一个新的SocketChannel来进行数据读写。
Buffer(缓冲区)
Buffer是一个高效的数据容器,在NIO中所有的数据操作都必须经过缓冲区,这点是和BIO不同的,BIO是直接将数据写到Stream对象中的。因为Stream对象的设计是按顺序一个字节一个字节的传送数据。虽然出于性能考虑,也可以传递字节数组,但是基本概念都是一个字节一个字节的传递数据。通道与之不同之处在于,通道会传送缓冲区的数据块,而且通道的基本概念就是按照一个数据块一个数据块的去读和写。所以也可以将缓冲区理解为一个字节数组,专门用来存储以及准备好出入通道的字节。
如下图:
如上图所示,无论是客户端发送和接收数据,还是服务端接收和相应数据,都是从缓冲区中进行数据操作的。
在Java中除了boolean外,所有的基本数据类型都有特定的Buffer子类:
ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer。网络程序几乎只会使用ByteBuffer,但程序偶尔也会使用其他类型来取代ByteBuffer。
除了数据列表外,每个缓冲区都记录了信息的4个关键部分。无论是何种类型,都有相同的方法来获取和设置这些值:
-
位置(position)
缓冲区中将读取或写入的下一个位置。这个位置从0开始计,最大值等于缓冲区的大小。可以用下面两个方法获取和设置。
public final int position(); public final Buffer position(int newPosition);
-
容量(capacity)
缓冲区可以保存的最大数目。容量值在创建缓冲区时设置,此后不能改变。
可以用以下方法读取:
public final