定义:Channel表示IO源与目标打开的连接。类似于传统的"流",但是channel本身不能直接访问数据,只能与buffer进行交互。channel本身不存储数据,因此需要配合缓冲区进行传输
try {
FileInputStream fileInputStream = new FileInputStream("D:\\books.txt");
FileChannel fileChannel = fileInputStream.getChannel(); // 打开一个通道
// 面向缓冲区编程 定义一个缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(10); // 定义缓冲区字节数的大小
int read = fileChannel.read(byteBuffer); // channel不直接访问数据 而是从缓冲区中 读数据
System.out.println(new String(byteBuffer.array()));
// 写数据
FileOutputStream fileOutputStream = new FileOutputStream("D:\\books_cp.txt");
FileChannel outputStreamChannel = fileOutputStream.getChannel();
// ByteBuffer buffer = ByteBuffer.allocate(10); // 定义缓冲区字节数的大小
// buffer.put("Hello NIO".getBytes()); // 往缓冲区写数据
byteBuffer.flip(); // 把读模式转换成写操作
outputStreamChannel.write(byteBuffer);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
在这里 读的操作使用到了缓冲流,具体关于传统stream流的写法可以参考博客:JAVA IO流之基础IO-CSDN博客
// 基于磁盘文件的字节输入输出流
//1.将磁盘文件使用Input流输入到内存 然后再写入到磁盘文件中
FileInputStream inputStream = new FileInputStream("D:\\books.txt");
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
FileOutputStream fileOutputStream = new FileOutputStream("D:\\hello1.txt");
int i = 0;
while ((i = bufferedInputStream.read()) != -1) {
fileOutputStream.write(i);
}
inputStream.close();
fileOutputStream.close();
bufferedInputStream.close();
1. 是否可以双向传输: 从上面代码可以看出来,channel读和写都是使用的FileChanel,而Stream流中读使用的是InputStream 写使用的是outputStream? 因此channel是支持双向传输的,而stream流只支持单向传输
2.是否必须面向缓冲区: channel本身不直接访问数据 因此channel必须面向缓冲区来进行数据的读写,Stream流可以直接读写数据,从代码里也可以看出,写入数据时没用到缓冲区也是可以的。
3.是否支持异步:channel支持异步,stream不支持异步。例如在多路复用器中,使用多路复用器selector监听chnanel时,并不需要像BIO一样,阻塞到channel有数据才返回。而是channel有数据了会主动去通知Seector,让Selector再去处理。 (BIO和多路复用器的具体用法可以参考博客JAVA IO流之网络IO-CSDN博客)
一个文件的读写的正常交互逻辑是怎么样的?可以先看下面的一幅图
第一步:向磁盘发出请求,我要读你的数据了
第二步:把磁盘上的文件 写入到内核的一个缓冲区
第三步:由内核的缓冲区在复制一份到用户空间的缓冲区,就是最后我们可以看到的
那么什么是零拷贝?零拷贝就是对这一个过程的优化,将内核空间的缓冲区和用户空间的缓冲区构造一个缓冲区,那么就相当于把第二步和第三步融合在了一起,提高效率。
try {
FileChannel inChannel = FileChannel.open(Paths.get("D:\\books.txt"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("D:\\bookes.txt"), StandardOpenOption.READ, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
MappedByteBuffer inMappedByteBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outMappedByteBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
byte[] bytes = new byte[inMappedByteBuffer.limit()];
inMappedByteBuffer.get(bytes);
outMappedByteBuffer.put(bytes);
inChannel.close();
outChannel.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
分析: 利用mmap虚拟的一个共享内存,从上面代码 通道channel只跟 mmapbuffer这一个缓冲区打交道,直接从缓冲区中取出数据来进行写入,也可以理解为省去了用户空间的部分。