Java NIO是一个用来替代标准Java IO API的新型数据传递方式,像现在分布式架构中会经常存在他的身影。其比传统的IO更加高效,非阻塞,异步,双向
NIO主体结构
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,数据量<capacity时,limit<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网站
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。