io with java
Table of Contents
目的
本文目的对本人对io的理解纠正
filechannel
BIO,NIO貌似NIO就是非阻塞,那么看如下代码:
RandomAccessFile file = new RandomAccessFile("/home/paul/opensource/yutak-dev/demo.txt","rw");
FileChannel channel = file.getChannel();
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
buffer.clear();
channel.read(buffer);
buffer.flip();
byte[] array = new byte[buffer.limit()];
buffer.get(array);
char[] chars = new char[array.length];
for (int i = 0; i < array.length; i++) {
chars[i] = (char) array[i];
}
System.out.println(String.valueOf(chars));
这是我的第一个误区,就是NIO不应该不阻塞吗?
实际上:
这里看了不少的blog,概念很多,我认为还是根据场景分析吧,记忆概念提升不高
IO对于调用者来说,阻塞就是调用代码时,需要立即获得结果,同步执行,我们经常作为client
使用,比如等待sql,等待http response.etc就是这种同步使用,非阻塞的话实际上要使用异步api,CompletableFuture
很经典,当然其存在whenCompleteAsync
的api
,实际上就是非当前thread
执行回调逻辑,下面这段code执行结果:world hello,其实client这边考虑的很简单,要么一直等待,要么通过事件驱动,挂载一个Handler,数据到达后通知
- 异步阻塞
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 13;
}).whenComplete((a,t)->{
System.out.println(a);
});
TimeUnit.SECONDS.sleep(5);
- 异步非阻塞
CompletableFuture.supplyAsync(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 13;
}).whenComplete((a,t)->{
System.out.println(a);
});
TimeUnit.SECONDS.sleep(10);
MappedByteBuffer
api
角度上是针对fileChannel
的map
函数
RandomAccessFile file = new RandomAccessFile("/home/paul/opensource/yutak-dev/demo.txt", "rw");
MappedByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, file.length());
for (int i = 0; i < 1024 * 1024 * 1024; i += 4*1024) {
buffer.get(i);
}
fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, _GB);
,进行映射是一个消耗极少的ops
,此时并不意味着 1G 的文件被读进了pageCache
mmap
映射的过程可以理解为一个lazy load
,只有get()
时才会触发缺页中断,DMA
load进OS- 预读大小是由
OS
算法决定的,可以默认4kb
,所以说如果想在这个实验中加载内存到pageCache
实际上是要不断get()
mmap
的生命周期可以视为:map(映射)
,get/load (缺页中断)
,clean(回收)
,很多文章将mmap
的提升归功于减少cpu拷贝
,实际上我认为是用户态trap
进入内核态
的消除,最常见的情况就是鼠标的轮询率,高轮询率的时候,系统占用陡增,并且可能出现卡顿,然而如果说,对buffer的数据read()
,虽然无需换态,但是依然需要从page cache
读数据进入userCache
,总结下,就是一段自定义的堆外内存
public static void main(String[] args) throws Exception {
long s = System.currentTimeMillis();
do3();
System.out.println("cost time mills:"+ (System.currentTimeMillis() - s));
}
public static void do1() throws Exception {
RandomAccessFile file = new RandomAccessFile("/home/paul/opensource/yutak-dev/demo.txt", "rw");
MappedByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, file.length());
for (int i = 0; i < 1024 * 1024 * 1024; i ++) {
buffer.get(0);
}
}
public static void do3() throws Exception {
RandomAccessFile file = new RandomAccessFile("/home/paul/opensource/yutak-dev/demo.txt", "rw");
FileChannel channel = file.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1);
for (int i = 0; i < 1024 * 1024 * 1024 ; i ++) {
channel.read(buffer);
}
}
这两段逻辑,如果调用次数较少,实际上是不分伯仲的,但是将次数调大,这个数据1GB,filechannel几乎停滞,本实验并不严谨,但是在模拟的情况下,二者已经跨越多个数量级,足以保证实验的基本正确性,,所以说,高并发的时候,换态性能损耗最大
file system
给出一张linux
的图片供以后温习