java io类型_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对象的设计是按顺序一个字节一个字节的传送数据。虽然出于性能考虑,也可以传递字节数组,但是基本概念都是一个字节一个字节的传递数据。通道与之不同之处在于,通道会传送缓冲区的数据块,而且通道的基本概念就是按照一个数据块一个数据块的去读和写。所以也可以将缓冲区理解为一个字节数组,专门用来存储以及准备好出入通道的字节。

如下图:

1f55b146b1018f05ad6ed72e55e9905f.png

如上图所示,无论是客户端发送和接收数据,还是服务端接收和相应数据,都是从缓冲区中进行数据操作的。

在Java中除了boolean外,所有的基本数据类型都有特定的Buffer子类:

ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer。网络程序几乎只会使用ByteBuffer,但程序偶尔也会使用其他类型来取代ByteBuffer。

除了数据列表外,每个缓冲区都记录了信息的4个关键部分。无论是何种类型,都有相同的方法来获取和设置这些值:

位置(position)

缓冲区中将读取或写入的下一个位置。这个位置从0开始计,最大值等于缓冲区的大小。可以用下面两个方法获取和设置。

public final intposition();public final Buffer position(int newPosition);

容量(capacity)

缓冲区可以保存的最大数目。容量值在创建缓冲区时设置,此后不能改变。

可以用以下方法读取:

public final int capacity();

限度(limit)

缓冲区中可访问数据的末尾位置。只要不改变限度,就无法读/写超过这个位置的数据,即使缓冲区有更大的容量也没有用。限度可以用下面两个放获取和设置。

public final intlimit();public final Buffer limit(int newLimit);

标记(mark)

缓冲区中客户端指定的索引。通过调用mark()可以将标记设置为当前位置。调用reset()可以将当前位置设置为所标识的位置。

public finalBuffer mark() ;public final Buffer reset();

如果将位置设置为低于现有标记,则丢弃这个标记。

与读取InputStream不同,读取缓冲区实际上不会以任何方式改变缓冲区中的数据。只可能向前或向后设置位置,从而可以从缓冲区中某个特定位置开始读取。类似的程序可以调整限度,从而控制将要读取的数据的末尾。只有容量是固定的。

Selector(选择器)

Selector是Java NIO中最重要的一部分,Selector的作用就是用单线程来轮询处理注册的Channel,一旦哪个Channel的数据准备就绪了,就可以进行处理了。

如下图:

253d24cfadfa43cc915642484b2ac21d.png

使用Selector,要先向Selector中注册Channel, 然后调用它的select()方法,这个方法会一直阻塞到某个注册的Channel中的事件准备就绪。一旦select()方法返回,线程就可以处理这些事件了,比如新的连接进入,数据接收等。

Selector类并没有注册新通道的方法,register()方法是在SelectableChannel类中声明。SelectableChannel类是实现自Channel接口的,它支持将Channel注册到Selector中。

SelectableChannel提供了两个注册Channel的方法:

public abstract SelectionKey register(Selector sel, intops, Object att)throwsClosedChannelException;public final SelectionKey register(Selector sel, intops)throws ClosedChannelException;

第一个参数代表通道要向哪个选择器注册。第二个参数是SelectionKey类中的一个命名常量,标识所注册的操作。

SelectionKey定义了4个命名位常量,用户选择操作类型:

SelectionKey.OP_READ;

SelectionKey.OP_WRITE;

SelectionKey.OP_CONNECT;

SelectionKey.OP_ACCEPT;

当一个通道需要在同一个选择器中关注多个操作,只需要用户位“或”操作符组合这些常量即可。

channel.register(selector,SelectionKey.OP_READ | SelectionKey.OP_WRITE);

第三个参数是可选的,代表键的附件。这个参数通常用户存储链接状态,例如:如果要实现一个Web服务器,可能要附加一个FileInputStream或FileChannel,这个流或通道连接到服务器提供给客户端的本地文件。

例子:

复制文件的读写操作,可以用来举例说明NIO的一个大致过程。

public static void copyFileByNIO(String src,String dst) throwsIOException {//声明源文件和目标文件

RandomAccessFile aFile = new RandomAccessFile(src, "rw");

RandomAccessFile bFile= new RandomAccessFile(dst, "rw");//获得传输通道channel

FileChannel inChannel =aFile.getChannel();

FileChannel outChannel=bFile.getChannel();//获得容器buffer

ByteBuffer buffer= ByteBuffer.allocate(1024);while(true){//判断是否读完文件

int eof =inChannel.read(buffer);if(eof==-1){break;

}//重设一下buffer的position=0,limit=position

buffer.flip();//开始写

outChannel.write(buffer);//写完要重置buffer,重设position=0,limit=capacity

buffer.clear();

}

inChannel.close();

outChannel.close();

aFile.close();

bFile.close();

}

AIO

AIO这次就不介绍了,我后续要单独的拿出一整篇来介绍AIO。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值