NIO初探

来源:互联网 发布:windows10引导ubuntu 编辑:程序博客网 时间:2024/06/06 03:32

ByteBuffer

ByteBuffer有两个实现方式:HeapByteBuffer是基于Java堆的实现,而DirectByteBuffer则使用了unsafe的API进行了堆外的实现。

bytebuffer的创建:

ByteBuffer allocate(int capacity);ByteBuffer allocateDirect(int capacity) //创建一个direct的ByteBuffer,这样的ByteBuffer在参与IO操作时性能会更好。ByteBuffer wrap(byte [] array)ByteBuffer wrap(byte [] array, int offset, int length) //把一个byte数组或byte数组的一部分包装成ByteBuffer。

allocateDirect方法创建的是直接缓冲区:
JVM虚拟机会直接在此缓冲区上执行本机IO操作,也就是说,在每次调用基础操作系统的一个本机IO之前或者之后,虚拟机都会避免将缓冲区的内容复制到中间缓冲区(或者从中间缓冲区复制内容)。直接缓冲区进行分配和取消分配所需要的成本往往比间接缓冲区要高,直接缓冲区的内容可以驻留 在常规的垃圾回收堆之外,因此,它们对应用程序的内容需求量造成的影响可能并不明显,所以建议将直接缓冲区主要分配给那些容易受基础系统的本 机IO操作影响的大型、持久的缓冲区。一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好处的时候分配它们。

API使用

  1. 当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式。

  2. 一旦读完了所有的数据,就需要清空缓冲区。有两种方式能清空缓冲区:调用clear()或compact()方法。clear()方法会清空整个缓冲区。compact()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。

Buffer的capacity,position和limit

capacity:容量大小
另外两个分为在读模式下还是写模式下。
position:
当你写数据到Buffer中时,position表示当前的位置。初始的position值为0.当一个byte、long等数据写到Buffer后, position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity – 1.

当读取数据时,也是从某个特定位置读。当将Buffer从写模式切换到读模式,position会被重置为0. 当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。
limit:
在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。 写模式下,limit等于Buffer的capacity。
当切换Buffer到读模式时, limit表示你最多能读到多少数据。

分配:

ByteBuffer buf = ByteBuffer.allocate(48);

从channel写到buffer

int bytesRead = inChannel.read(buf); //写bufferbuffer.put(127);buffer.flip();flip方法将Buffer从写模式切换到读模式。调用flip()方法会将position设回0,并将limit设置成之前position的值。

从buffer中读取数据

//read from buffer into channel.int bytesWritten = inChannel.write(buf);//或者byte aByte = buf.get();//Buffer.rewind()将position设回0

mark()与reset()方法

通过调用Buffer.mark()方法,可以标记Buffer中的一个特定position。之后可以通过调用Buffer.reset()方法恢复到这个position

NIO的优点

  1. 客户端发起的连接操作都是异步的。可以在多路复用器上注册OP_CONNECT事件等待后续结果。
  2. SocketChannel的读写操作都是异步的。
  3. 线程模型的优化。由于JDK的Selector在Linux等主流操作系统上通过epoll实现,他没有连接句柄数的限制(只受限于操作系统的最大句柄数或者对单个进程的句柄限制)。这意味着一个Selector线程可以同时处理成千上网个连接,而且性能不会随着客户端的增加而线性下降。

AIO编程

NIO2.0的异步套接字通道是真正的异步非阻塞IO,对应于UNIX网络编程中的事件驱动IO,它不需要通过多路复用器对注册的通道进行轮询即可实现异步读写。

Scatter/Gather

分散(scatter)从Channel中读取是指在读操作时将读取的数据写入多个buffer中

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

athering Writes是指数据从多个buffer写入到同一个channel

ByteBuffer header = ByteBuffer.allocate(128);ByteBuffer body   = ByteBuffer.allocate(1024);//write data into buffersByteBuffer[] bufferArray = { header, body };channel.write(bufferArray);

通道之间的数据传输

如果两个通道中有一个是FileChannel,那你可以直接将数据从一个channel传输到另外一个channel。

RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");FileChannel      fromChannel = fromFile.getChannel();RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");FileChannel      toChannel = toFile.getChannel();long position = 0;long count = fromChannel.size();toChannel.transferFrom(position, count, fromChannel);

transferTo()方法将数据从FileChannel传输到其他的channel中。

RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");FileChannel      fromChannel = fromFile.getChannel();RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");FileChannel      toChannel = toFile.getChannel();long position = 0;long count = fromChannel.size();fromChannel.transferTo(position, count, toChannel);