Java NIO使用分析

1、概述

传统的Java io是面向流Stream的,阻塞IO;而nio则是面向缓冲区buffer的,非阻塞的io。区别是流中数据不能移动,而buffer可以移动。

Java nio选择器Selectors允许单个线程监控多个通道的输入和输出。

Java缓冲区Buffer,本质上其实是一个内存块,可以往里面写入数据,可以从里面读取数据。其被包装成NIO buffer对象,便于操作。
Buffer 一般和Channel配合使用。

一般使用步骤如下:

1.数据写入缓冲区,缓冲区会记录写入数据的大小。
2.调用flip方法,从写入模式转至读取模式。
3.从缓冲区读取数据
4.调用clear或者compact方法清除数据

2 Channel

2.1 channel与stram区别

channel 既可以读也可以写,而stream只能读或者写二选一。
channel可以异步读写
channel只能从buffer中进行读写

2.2 常见channel

FileChannel 从文件中读取数据

DatagramChannel 从UDP中进行读写

SocketChannel 从TCP中进行读写

ServerSocketChannel 允许监听进来的TCP连接,每个进来的连接都会创建一个SocketChannel

2.3用法

xxx.getChannel()方法获取一个Channel,然后和buffer配合读写数据。

3 Buffer容量、位置、限制

capacity:容量,buffer的大小。创建时确定,无法更改。
position:位置,被读取或写入的下一个元素的位置。
    写模式:写入数据的位置,默认为0,最大可以是capacity-1
    读模式:从position开始读取,然后position会自动增长,指向下一个读取位置。
        从写模式切换到读模式时,会将position重置为0.
        可以通过position(int)方法重设position
limit:限制,缓冲区中可以使用的元素个数。
    写模式:limit 等于capacity,表示你可以写多少数据到缓冲区,l
    读模式:limit表示你可以从缓冲区读取多少数据。
    从写模式切换到读模式的时候,limit的值为写模式的position的值。
    可以通过limit(int)方法重设limit
Mark 标记:
    被记录的位置,调用mark方法时设置(mark=position),默认为-1

buffer实际存储的对象时数组,其是非线程安全的

4 NIO buffer类型

java nio缓冲区有以下几种:

ByteBuffer
MappedByteBuffer
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer

5 缓冲区的使用

5.1 创建

创建完成后,默认就是写模式。

ByteBuffer.allocate(int size) 在JVM中分配size字节大小的容量。
ByteBuffer. allocateDirect(int size) 在JVM之外,系统内存中分配size字节大小的的容量。
XXXBuffer.wrap(byte[] array) 使用已经分配的数组作为buffer管理。

5.2写缓冲区

方式一:从通道Channel里面写数据到缓冲区。

RandomAccessFile file = new RandomAccessFile("data.txt", "rw");
FileChannel fChannel = file.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(48);
//从fChannel中读取数据写入到buffer中,byteReads是返回的写入数据的大小,-1表示写完所有数据
int byteReads = fChannel.read(buffer);

方式二:使用buffer的putXXX方法

RandomAccessFile file = new RandomAccessFile("data.txt", "rw");
FileChannel fChannel = file.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(48);
buf.put(127);

put方法有很多重载方式,支持各种方式的写入数据,具体可以查看Api。单参数的put方法会自动调整position的值,带有index参数的put,如put(int index, byte b)并导致任何属性的变化。

5.3 flip

flip方法用于切换缓冲区从写模式到读模式。

调用flip 方法会讲position设置为0,同时将limit设置为之前的position的值。

5.4 读缓冲区

方式一:将数据从buffer读取到channel

//从buffer读取数据到channel,返回值byteReads为读取的数据大小,-1表示读取完所有数据。
byteReads = fChannel.read(buffer);  

方式二:使用getXXX方法读取数据

getXXX方法同put方法类似

buf.get()

可以使用rewind()方法,将position设置为0,然后重新从buffer读取数据。get()会导致position的变化,get(int index) 不会导致任何属性的变化。

5.5 数据清除

读取完数据后,若需要再次写入,可以调用clearcompact方法清除数据

clear方法将position设置为0,将limit设置为capacity,实际并没有清空数据,只是重置了写入的位置。

compact方法将未读取的数据移动到缓冲区开始位置,将position设置在最后的未读元素后面,limit仍然设置为capacity。compact使得buffer可以再次写入,append到之前未读数据后面。

5.6 设置标记

mark()方法可以在buffer中设置一个标记,记录当前position,然后若想回到这个标记的位置调用reset()即可。

5.7 buffer之间的比较

equals()比较buffer中剩余部分元素,其判断相等的条件:

1、两个buffer有数据相同的类型
2、buffer剩余元素的数目相等
3、buffer剩余元素一一相等

compareTo():比较两个buffer的剩余元素,bufferA比bufferB小的情况如下:

1、bufferA的元素小于bufferB中对应位置的元素
2、所有元素相等,但bufferA比bufferB元素个数少。

5.8 order方法

获取以及设置读写的字节序。

6 Scatter/Gather

Java NIO支持scatter/gather(分散/聚集),用于描述从通道读取和写入通道。使用场景:消息数据各部分需要分开传输,比如消息头和消息体组成的消息。

分散读是在读操作的时候,读取的数据被写入多个buffer中。scatter将数据从通道写入多个buffer。

聚集写,在写入多个buffer到一个通道,此时gather将数据从多个buffer写入通道。

6.1 read

read 把通道里的数据按照buffers数组中的顺序填充到相应的buffer中。当第一个buffer填充满后,才会填充下一个buffer。

ByteBuffer head = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
ByteBuffer[] buffers = {head, body}; 
channel.read(buffers);

6.2 write

write方法按照数组中的顺序,一个个的讲buffer中的内容写入Channel中。而且写入的时候是仅仅将buffer的position和limit之间的元素写入。而不是全部写入。

ByteBuffer head = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
ByteBuffer[] buffers = {head, body}; 
channel.write(buffers);

你可能感兴趣的:(java,数据,nio)