以前我们对文件的读写都使用什么呢?
如果是字节流,我们有FileInputStream和FileOutputStream;如果是字符流我们有FileReader和FileWriter;
然而,本文的主角RandomAccessFile可以同时支持对文件的读写,并且可以支持随机访问文件,即访问文件的任意位置内容。如此,我们可以使用多线程的方式来对同一个文件进行读写操作,大大提高了IO的效率。
首先我们来看一个实例,来熟悉一下RandomAccessFile的使用:
test.txt
123456789
Test.java
public static void main(String[] args) throws IOException {
// r-只读模式;rw-读写模式
RandomAccessFile raf = new RandomAccessFile("test.txt","rw");
log.info("文件中总字节的长度为:{}", raf.length());
log.info("当前读写指针的位置为:{}", raf.getFilePointer());
// 重置指针到指定位置
raf.seek(3);
log.info("当前读写指针的位置为:{}", raf.getFilePointer());
raf.close();
}
我们可以通过指定模式来实例化RandomAccessFile对象,这里先只了解最常用的两种模式即可。
seek的作用在于可以重置读写指针的位置,这是一个非常强大的功能,我们再来看一个例子:
public static void main(String[] args) throws IOException {
// r-只读模式;rw-读写模式
RandomAccessFile raf = new RandomAccessFile("test.txt","rw");
// 在首部插入hello
raf.write("hello".getBytes());
// 重置指针到hello之后
raf.seek(5);
raf.writeBytes(",world");
raf.close();
}
此时,test.txt中的内容就变为:
hello,world
我们再来一个多线程复制文件的例子:
CopyFileThread.java
@Slf4j
public class CopyFileThread implements Runnable {
private RandomAccessFile inFile;
private RandomAccessFile outFile;
private Long start;
private Long end;
private byte[] readBytes;
private int readIndex;
public CopyFileThread(String inFile, String outFile, Long start, Long end) throws FileNotFoundException {
this.start = start;
this.end = end;
this.inFile = new RandomAccessFile(inFile, "rw");
this.outFile = new RandomAccessFile(outFile, "rw");
// 默认设置一个3字节数组,用来存放每次复制时读取的内容
this.readBytes = new byte[3];
// 每次复制时读取的字节数
this.readIndex = 0;
}
@Override
public void run() {
try {
// 定位到当前线程分工需要开始读取的地方
inFile.seek(start);
// 定位到当前线程分工需要开始写出的地方
outFile.seek(start);
// 当前线程分工范围内,且文件未读取完时循环读取
while(start < end && readIndex != -1){
// 防止当前线程越界读取别的线程所负责范围内的字节
if((end - start) >= readBytes.length){
// 读取readBytes大小的字节
this.readIndex = inFile.read(readBytes);
} else {
// 读取剩余应该读取内容的字节数,此时end-start肯定小于readBytes.length,可以放心地强转
this.readIndex = inFile.read(readBytes, 0, (int)(end - start));
}
// 将读到的数据从头到尾进行写出
outFile.write(readBytes, 0, readIndex);
start += readIndex;
}
} catch (IOException e) {
log.info("文件复制出现异常:", e);
} finally {
try {
inFile.close();
outFile.close();
} catch (IOException e) {
log.info("随机文件流关闭出现异常:", e);
}
}
}
}
Test.java
@Slf4j
public class Test {
private static final String IN_FILE = "test1.txt";
private static final String OUT_FILE = "test2.txt";
private static final Integer THREAD_COUNT = 2;
public static void main(String[] args) throws IOException {
startCopyFile(IN_FILE, OUT_FILE, THREAD_COUNT);
}
private static void startCopyFile(String inFile, String outFile, int threadCount) throws FileNotFoundException {
// 每个线程需要负责拷贝的字节长度
long segmentFileLength = new File(IN_FILE).length() / threadCount;
for (int i = 0; i < threadCount; i++) {
// 为每个线程进行分工
new Thread(new CopyFileThread(IN_FILE, OUT_FILE, i*segmentFileLength, (i+1)*segmentFileLength)).start();
}
}
}
关于多线程如何调试,可以参考这篇文章《IDEA调试技巧进阶》
网友评论