示例代码: 从一个文件拷贝到另一个文件
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
public class ChannelDemo {
public static void main(String args[]) throws IOException {
String relativelyPath = System.getProperty("user.dir");
FileInputStream input = new FileInputStream(relativelyPath + "/testin.txt");
ReadableByteChannel source = input.getChannel();
FileOutputStream output = new FileOutputStream(relativelyPath + "/testout.txt");
WritableByteChannel destination = output.getChannel();
copyData(source, destination);
source.close();
destination.close();
System.out.println("Copy Data finished.");
}
private static void copyData(ReadableByteChannel src, WritableByteChannel dest) throws IOException {
ByteBuffer buffer = ByteBuffer.allocateDirect(20 * 1024);
while (src.read(buffer) != -1) {
// The buffer is used to drained
buffer.flip();
// keep sure that buffer was fully drained
while (buffer.hasRemaining()) {
dest.write(buffer);
}
buffer.clear(); // Now the buffer is empty, ready for the filling
}
}
}
缓冲区
大多数使用的缓冲区类型是ByteBuffer。上图的各个缓冲区代表了各个缓冲区的对应的IO类型。
分配缓冲区
在每个Buffer类中,allocate()方法用于分配缓冲区。
示例
//ByteBuffer分配容量为28字节的例子:
ByteBuffer buf = ByteBuffer.allocate(28);
//CharBuffer分配空间大小为2048个字符
CharBuffer buf = CharBuffer.allocate(2048);
从缓冲区读数据
- 方法一, 使用get方法
- 方法二,将buffer中数据读到通道
//方法一,使用get()方法
byte temp = buf.get();
//方法二,将buffer中数据读入通道
int temp = inchannel.write(buffer);
从缓冲区写数据
- 方法一:使用put()方法将数据写入缓冲区。
- 方法二:将数据从Channel写入缓冲区。
示例使用BufferedReader
public class Test{
public static void main(String[] args){
string PATH = "D:\\a.txt";
BufferedReader bd = null;
try{
InputStream in = Files.newInputStream(PATH);
bd = new BufferedReader(new InputStreamReader(in));
System.out.println("%s", bd.readline());
}catch(Exception e){
}finally{
try{
bd.close();
}catch(Exception e ){
}
}
}
}
分散读取/聚集写入
分散读取:一个通道可以从多个缓冲区读取数据
聚集写入:多个缓冲区可以写入一个通道
通道之间的数据传输
通道之间的数据传输在FileChannel类中的两种方法是:
- FileChannel.transferTo()方法
- FileChannel.transferFrom()方法
- transferTo()方法用来从FileChannel到其他通道的数据传输。
- transferFrom()方法允许从源通道到FileChannel的数据传输。
Java NIO选择器
选择器(Selector)用于使用单个线程处理多个通道。 因此,它需要较少的线程来处理这些通道。
PPS: java 读取文件几种方法
mmap
github一个作者实现的大文件MMAP按行读取:
MappedByteBuffer buffer = fc.map(MapMode.READ_WRITE, 0, 1000);
force()方法,是将内存的更新的内容刷到磁盘中。
作为NIO的一个重要的功能,Mmap方法为我们提供了将文件的部分或全部映射到内存地址空间的能力,同当这块内存区域被写入数据之后会变成脏页,操作系统会用一定的算法把这些数据写入到文件中,而我们的java程序不需要去关心这些。这就是内存映射文件的一个关键优势,即使你的程序在刚刚写入内存后就挂了,操作系统仍然会将内存中的数据写入文件系统。
另外一个更突出的优势是共享内存,内存映射文件可以被多个进程同时访问,起到一种低时延共享内存的作用。
java浅析mmap和Direct Buffer
示例:Java MMAP 与 FileChannel操作文件对比
package com.github.hashZhang.scanfold.jdk.file;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Random;
public class FileMmapTest {
public static void main(String[] args) throws Exception {
//记录开始时间
long start = System.currentTimeMillis();
//通过RandomAccessFile的方式获取文件的Channel,这种方式针对随机读写的文件较为常用,我们用文件一般是随机读写
RandomAccessFile randomAccessFile = new RandomAccessFile("./FileMmapTest.txt", "rw");
FileChannel channel = randomAccessFile.getChannel();
System.out.println("FileChannel初始化时间:" + (System.currentTimeMillis() - start) + "ms");
//内存映射文件,模式是READ_WRITE,如果文件不存在,就会被创建
MappedByteBuffer mappedByteBuffer1 = channel.map(FileChannel.MapMode.READ_WRITE, 0, 128 * 1024 * 1024);
MappedByteBuffer mappedByteBuffer2 = channel.map(FileChannel.MapMode.READ_WRITE, 0, 128 * 1024 * 1024);
System.out.println("MMAPFile初始化时间:" + (System.currentTimeMillis() - start) + "ms");
start = System.currentTimeMillis();
testFileChannelSequentialRW(channel);
System.out.println("FileChannel顺序读写时间:" + (System.currentTimeMillis() - start) + "ms");
start = System.currentTimeMillis();
testFileMMapSequentialRW(mappedByteBuffer1, mappedByteBuffer2);
System.out.println("MMAPFile顺序读写时间:" + (System.currentTimeMillis() - start) + "ms");
start = System.currentTimeMillis();
try {
testFileChannelRandomRW(channel);
System.out.println("FileChannel随机读写时间:" + (System.currentTimeMillis() - start) + "ms");
} finally {
randomAccessFile.close();
}
//文件关闭不影响MMAP写入和读取
start = System.currentTimeMillis();
testFileMMapRandomRW(mappedByteBuffer1, mappedByteBuffer2);
System.out.println("MMAPFile随机读写时间:" + (System.currentTimeMillis() - start) + "ms");
}
public static void testFileChannelSequentialRW(FileChannel fileChannel) throws Exception {
byte[] bytes = "测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1".getBytes();
byte[] to = new byte[bytes.length];
//分配直接内存,减少复制
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bytes.length);
//顺序写入
for (int i = 0; i < 100000; i++) {
byteBuffer.put(bytes);
byteBuffer.flip();
fileChannel.write(byteBuffer);
byteBuffer.flip();
}
fileChannel.position(0);
//顺序读取
for (int i = 0; i < 100000; i++) {
fileChannel.read(byteBuffer);
byteBuffer.flip();
byteBuffer.get(to);
byteBuffer.flip();
}
}
public static void testFileMMapSequentialRW(MappedByteBuffer mappedByteBuffer1, MappedByteBuffer mappedByteBuffer2) throws Exception {
byte[] bytes = "测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2".getBytes();
byte[] to = new byte[bytes.length];
//顺序写入
for (int i = 0; i < 100000; i++) {
mappedByteBuffer1.put(bytes);
}
//顺序读取
for (int i = 0; i < 100000; i++) {
mappedByteBuffer2.get(to);
}
}
public static void testFileChannelRandomRW(FileChannel fileChannel) throws Exception {
try {
byte[] bytes = "测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1".getBytes();
byte[] to = new byte[bytes.length];
//分配直接内存,减少复制
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bytes.length);
//随机写入
for (int i = 0; i < 100000; i++) {
byteBuffer.put(bytes);
byteBuffer.flip();
fileChannel.position(new Random(i).nextInt(bytes.length*100000));
fileChannel.write(byteBuffer);
byteBuffer.flip();
}
//随机读取
for (int i = 0; i < 100000; i++) {
fileChannel.position(new Random(i).nextInt(bytes.length*100000));
fileChannel.read(byteBuffer);
byteBuffer.flip();
byteBuffer.get(to);
byteBuffer.flip();
}
} finally {
fileChannel.close();
}
}
public static void testFileMMapRandomRW(MappedByteBuffer mappedByteBuffer1, MappedByteBuffer mappedByteBuffer2) throws Exception {
byte[] bytes = "测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2".getBytes();
byte[] to = new byte[bytes.length];
//随机写入
for (int i = 0; i < 100000; i++) {
mappedByteBuffer1.position(new Random(i).nextInt(bytes.length*100000));
mappedByteBuffer1.put(bytes);
}
//随机读取
for (int i = 0; i < 100000; i++) {
mappedByteBuffer2.position(new Random(i).nextInt(bytes.length*100000));
mappedByteBuffer2.get(to);
}
}
}