java doublebuffer_JAVA_NIO系列——Channel和Buffer详解

Java NIO是一个用来替代标准Java IO API的新型数据传递方式,像现在分布式架构中会经常存在他的身影。其比传统的IO更加高效,非阻塞,异步,双向

NIO主体结构

52e759c765f4155c1565f848e085e33b.png

Java NIO的主要构成核心就是Buffer、Channel和Selector这三个

对于Channel我想要提醒的是,Channel中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入

使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件

Channel

所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点象流

Channel的实现

FileChannel:从文件中读写数据

DatagramChannel:通过UDP读写网络中的数据

SocketChannel:通过TCP读写网络中的数据

ServerSocketChannel:监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel

Scatter/Gather

分散(scatter)从Channel中读取是指在读操作时将读取的数据写入多个buffer中。因此,Channel将从Channel中读取的数据“分散(scatter)”到多个Buffer中

聚集(gather)写入Channel是指在写操作时将多个buffer的数据写入同一个Channel,因此,Channel 将多个Buffer中的数据“聚集(gather)”后发送到Channel

通过这样的方式可以方便数据的读取,当你想要获取整个数据的一部分的时候,通过这种方式可以很快的获取数据

ByteBuffer header = ByteBuffer.allocate(128);

ByteBuffer body = ByteBuffer.allocate(1024);

ByteBuffer[] bufferArray = { header, body };

channel.read(bufferArray);

read()方法按照buffer在数组中的顺序将从channel中读取的数据写入到buffer,当一个buffer被写满后,channel紧接着向另一个buffer中写

transferFrom、transferTo

实现两个Channel之间相互连接,数据传递

public static void trainforNio() {

RandomAccessFile fromFile=null;

RandomAccessFile toFile=null;

try {

fromFile = new RandomAccessFile("src/nio.txt", "rw");

// channel获取数据

FileChannel fromChannel = fromFile.getChannel();

toFile = new RandomAccessFile("src/toFile.txt", "rw");

FileChannel toChannel = toFile.getChannel();

System.out.println(toChannel.size());

//position处开始向目标文件写入数据,这里是toChannel

long position = toChannel.size();

long count = fromChannel.size();

toChannel.transferFrom(fromChannel, position, count);

System.out.println(toChannel.size());

} catch (IOException e) {

e.printStackTrace();

} finally {

try {

if (fromFile != null) {

fromFile.close();

}

if (toFile != null) {

toFile.close();

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

transferFrom、transferTo作用是一样的,只是一个是tochannal调用,一个是fromchannnal调用

在实际的运用中可能存在源通道的剩余空间小于 count 个字节,则所传输的字节数要小于请求的字节数

在SoketChannel的实现中,SocketChannel只会传输此刻准备好的数据(可能不足count字节)。因此,SocketChannel可能不会将请求的所有数据(count个字节)全部传输到FileChannel中

看官一定要仔细看我栗子中的注释

Buffer

Buffer是一个缓存区,其会将Channel中的数据存储起来

Buffer的实现

ByteBuffer

CharBuffer

DoubleBuffer

FloatBuffer

IntBuffer

LongBuffer

ShortBuffer

MappedByteBuffer

capacity,position,limit

在讲解该主题之前,首先要明白读模式和写模式,无论是Channel还是Buffer都存在这两种模式,要理解这两种模式,第一步要明确主题是哪一个,是Channel还是Buffer。举个栗子,主角是Channel,读模式的含义就是从Buffer中获取数据,写模式就是将数据写入Buffer,对于Buffer则是相反。搞清楚这一点,理解下面的就要相对清楚一点

capacity:作为一个内存块,其就代表了当前Buffer能最多暂存多少数据量,存储的数据类型则是根据上面的Buffer对象类型,一旦Buffer满了,需要将其清空(通过读数据或者清除数据)才能继续写数据往里写数据

position:代表当前数据读或写处于那个位置。读模式:被重置从0开始,最大值可能为capacity-1或者limit-1,写模式:被重置从0开始,最大值为limit-1

limit:最多能往Buffer里写多少数据,limit大小跟数据量大小和capacity有关,读模式:数据量>capacity时,limit=capacity,数据量=capacity时,limit=capacity,数据量

import java.io.IOException;

import java.io.RandomAccessFile;

import java.nio.ByteBuffer;

import java.nio.channels.FileChannel;

public class Method {

public static void nio() {

RandomAccessFile aFile = null;

try {

aFile = new RandomAccessFile("src/nio.txt", "rw");

// channel获取数据

FileChannel fileChannel = aFile.getChannel();

// 初始化Buffer,设定Buffer每次可以存储数据量

// 创建的Buffer是1024byte的,如果实际数据本身就小于1024,那么limit就是实际数据大小

ByteBuffer buf = ByteBuffer.allocate(1024);

// channel中的数据写入Buffer

int bytesRead = fileChannel.read(buf);

System.out.println(bytesRead);

while (bytesRead != -1) {

// Buffer切换为读取模式

buf.flip();

// 读取数据

while (buf.hasRemaining()) {

System.out.print((char) buf.get());

}

// 清空Buffer区

buf.compact();

// 继续将数据写入缓存区

bytesRead = fileChannel.read(buf);

}

} catch (IOException e) {

e.printStackTrace();

} finally {

try {

if (aFile != null) {

aFile.close();

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

public static void main(String[] args) {

Method.nio();

Buffer读写数据步骤

写入数据到Buffer(fileChannel.read(buf))

调用flip()方法(buf.flip())

从Buffer中读取数据(buf.get())

调用clear()方法或者compact()方法(buf.compact())

Buffer方法

flip():将Buffer读模式切换到写模式,并且将position制为0

clear():清空整个缓冲区

compact():只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面

allocate(1024):初始化Buffer,设定的值就决定capacity值的大小

rewind():将position设回0,所以你可以重读Buffer中的所有数据。limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)

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

equals():当满足下面三个条件时,两个Buffer才是相等

有相同的类型(byte、char、int等)

Buffer中剩余的byte、char等的个数相等

Buffer中所有剩余的byte、char等都相同

只比较的是剩余的数据

compareTo():满足下列条件,则认为一个Buffer“小于”另一个Buffer

第一个不相等的元素小于另一个Buffer中对应的元素

所有元素都相等,但第一个Buffer比另一个先耗尽(第一个Buffer的元素个数比另一个少)

Selector

Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便

关于selector的知识内容比较多了,我打算在下一期进行详细说明

更多内容可以关注微信公众号,或者访问AppZone网站

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java NIO(New IO)是Java 1.4版本中引入的一组IO API,它提供了与标准IO不同的IO操作方式。与标准IO不同,Java NIO是面向缓冲区的IO操作方式,它能够更加高效地处理大量的数据。Java NIO提供了三个核心组件:Buffer(缓冲区)、Channel(通道)和Selector(选择器),这些组件在Java NIO中都有着非常重要的作用。 Java NIO中的Buffer是一个对象,它可以用来存储数据。相比于标准IO中的流,Buffer能够更加高效地处理数据,因为它可以直接与Channel进行交互,而不需要经过中间的InputStream或OutputStream。在Java NIO中,Buffer有七种类型:ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer和DoubleBuffer,它们分别用于存储不同类型的数据。 Java NIO中的Channel是用来进行数据读写的对象。与标准IO不同,Channel是双向的,可以同时进行读和写操作。Java NIO中提供了多种类型的Channel,每种类型都有着自己的特点和适用场景,例如FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel等等。 Java NIO中的Selector是用来进行多路复用的对象,它能够同时处理多个Channel的读写操作。Selector会不断地轮询注册在其上的Channel,如果某个Channel已经准备好进行读写操作,Selector就会通知相应的线程进行处理。 Java NIO的优点在于它能够更加高效地处理大量的数据。在标准IO中,每次IO操作都需要经过操作系统内核的缓冲区,这样会导致大量的系统开销。而Java NIO中的BufferChannel可以直接进行读写操作,避免了操作系统内核的缓冲区,从而提高了程序的性能。 总的来说,Java NIO提供了一种高效的IO操作方式,它的核心组件BufferChannel和Selector都有着非常重要的作用。如果需要处理大量的数据,使用Java NIO会比标准IO更加高效。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值