NIO归根结底就是使用通道的缓存与映射,以实现输入源与输出源的操作简化。
此篇不涉及Web端的NIO开发(选择器/就绪加载/管道)
要想理解NIO的缓存机制,就必须要了解三个参数:
参数 | 含义 |
---|---|
capacity | 缓冲区数组的总长度 |
position | 下一个要操作的数据元素的位置 |
limit | 缓冲区数组中限制长度的下一个位置:limit<=capacity |
而通道本身是用以下方法操作参数的:
方法名(Buffer) | 含义 |
---|---|
ByteBuffer.allocate(1024) | 生成/分配Buffer的空间大小 |
hasRemaining() | 检查当前位置与限制之间是否有元素 |
filp() | 读取与写入转换/其实就是限制长度和将position设置为0 |
clear() | 重置缓存区/归根结底就是重置上面的三个参数(和硬盘快速格式化原理差不多) |
compact() | 清除已读数据,将未读数据提前,新写入的数据将放在未读数据后面 |
get() | 从Buffer中获取一定长度的数组 |
put() | 往Buffer中添加一定长度的数组 |
方法名(Channel) | 含义 |
---|---|
read(Buffer) | 将数据读到Buffer中 |
write(Buffer) | 将Buffer的数据写入Channel中 |
transferFrom(inputChannel,起始位置,长度) | 将数据读取到inputChannel中 |
transferTo(起始位置,长度,outputChannel) | 将数据写入outputChannel中 |
map(MapMode,起始位置,长度) | 将这段数据映射到内存中 |
附加功能
MapMode类 | 含义 |
---|---|
READ_ONLY | 只读方式打开 |
READ_WEITE | 读写模式,修改将直接映射到源文件 |
PRIVATE | 专有模式,修改不会映射到源文件 |
方法名(Channel) | 含义 |
---|---|
lock() | 文件锁,顾名思义 |
字符转换
Charset cs = Charset.forName("UTF-8");//转为UTF-8格式
CharBuffer charbuffer = cs.decode(buffer);//先解析为Char
return cs.encode(charbuffer);//再编码为字节
附上文件复制代码
/** 普通缓存拷贝 */
public static void copyNio(File file, File toFile) throws IOException{
FileInputStream fis = null;
FileOutputStream fos = null;
FileChannel inChannel = null;
FileChannel outChannel = null;
try {
fis = new FileInputStream(file);
fos = new FileOutputStream(toFile);
inChannel = fis.getChannel();
outChannel = fos.getChannel();
long start = System.currentTimeMillis();
ByteBuffer buffer = ByteBuffer.allocate(0x100000);//创建缓存区
while(inChannel.read(buffer) != -1){//读取缓存区长度的数据(-1表示无数据)
buffer.flip();//重置position
outChannel.write(buffer);//写入position至limit中间的数据
buffer.flip();//重置position
}
long stop = System.currentTimeMillis();
System.out.println("nio:" + (stop-start));
} catch (Exception e) {
e.printStackTrace();
} finally {
if(fis != null)
fis.close();
if(fos != null)
fos.close();
if(inChannel != null)
inChannel.close();
if(outChannel != null)
outChannel.close();
}
}
/** 内存映射拷贝 */
public static void copyNioMapper(File file, File toFile) throws IOException, FileException{
//if(file.length() > ((long)1 << 32)) throw new FileException("文件大于两个G");
FileInputStream fis = null;
FileOutputStream fos = null;
FileChannel inChannel = null;
FileChannel outChannel = null;
MappedByteBuffer rb = null;
long SIZE = (long)1 << 30;//设置每个映射缓存为1G
try {
fis = new FileInputStream(file);
fos = new FileOutputStream(toFile);
inChannel = fis.getChannel();
outChannel = fos.getChannel();
long start = System.currentTimeMillis();
while(inChannel.position() != inChannel.size()){
if((inChannel.size() - inChannel.position()) < SIZE){//如果文件剩余大小小于1G
SIZE = (int) (inChannel.size() - inChannel.position());
}
rb = inChannel.map(MapMode.READ_ONLY, inChannel.position(), SIZE);//映射文件缓存
outChannel.write(rb);//直接写入
inChannel.position(inChannel.position() + SIZE);//设置inChannel的position
}
// rb = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
// outChannel.write(rb);
long stop = System.currentTimeMillis();
System.out.println("nioMapper:" + (stop-start));
} catch (Exception e) {
e.printStackTrace();
} finally {
if(rb != null)
rb.clear();
if(inChannel != null)
inChannel.close();
if(outChannel != null)
outChannel.close();
if(fis != null)
fis.close();
if(fos != null)
fos.close();
}
}
得出的结果是内存映射干不过缓存。。无论内存映射容量调节到多少,都干不过缓存,我怀疑我遇到了一个假的内存映射。。内存映射应该有更广的用途,复制文件什么的,缓存就足够了
代码案例里面没有涉及到get()/put()和transferFrom()/transferTo(),仅仅是文件复制的话不需要提高数据获取的精度,如果之后需要获取网络头信息啥的就会涉及。
实验环境:JDK1.8
后续更新。。。