1.针对大文件的读写的情景的缺点:
1.考虑到大文件的文件size过大导致读取速度慢,并且大文件读取进来后数据太多导致处理需要花费更多时间,并且IO过程中,CPU处于空闲,很大的浪费了CPU的性能。
2. IO需要将文件从磁盘通过DMA拷贝到内核缓冲区,然后从内核缓冲区拷贝到用户缓冲期,如果文件过大,那么这个拷贝也花时间
2.利用多线程和map内存映射加速读取
1.使用map避免文件从内核缓冲期复制到用户缓冲期。
2.加上多线程并行对一个文件进行IO和数据处理。
3. 考虑到通用性,线程之间进行同步需要避免读取重复的数据,因此使用一个volatile变量记录当前读取的index下标,然后每个线程读取数据使用cas更新下标。
java代码如下:
package List;
import javafx.concurrent.Worker;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Description: 实现多线程对同一个文件的并行读写
*
*
* @author Mayber
* @date Created on 2022/4/10
*/
public class ParalFile {
public AtomicInteger index = new AtomicInteger(0);
public long size;
public int buffer_size;
RandomAccessFile orign_raf;
RandomAccessFile target_raf;
FileChannel orignFileChannel;
FileChannel targetFileChannel;
public ParalFile(File orign,int buffer_size) throws FileNotFoundException {
RandomAccessFile orign_raf = new RandomAccessFile(orign,"rw");
// RandomAccessFile target_raf = new RandomAccessFile(target,"rw");
this.orignFileChannel = orign_raf.getChannel();
this.targetFileChannel = target_raf.getChannel();
this.buffer_size = buffer_size;
this.size = orign.length();
System.out.println("构造完毕");
}
class ReadTask implements Runnable{
@Override
public void run() {
// 这个任务中需要使用cas获取到当前的 index,并且读取index+buffer值,然后将index改为
int cur_index;
System.out.println("执行");
while((cur_index = index.get())<size){
int target_index = (cur_index+buffer_size)>size? (int)size :cur_index+buffer_size;
if(index.compareAndSet(cur_index,target_index+1)){
//如果cas 成功就进行读写操作
System.out.println(Thread.currentThread().getName()+"读取了cur_index"+cur_index+"到"+target_index+"的缓冲数据");
byte[] content = readFile(cur_index,target_index);
// 读取到了内容可以在下面进行一些别的处理操作
}
}
}
public byte[] readFile(int orign_index,int target_index){
// 读取文件,使用一个map内存映射进行读取,可以加速读取吧
MappedByteBuffer map;
byte[] byteArr = new byte[target_index-orign_index];
try {
map = orignFileChannel.map(FileChannel.MapMode.READ_ONLY,orign_index,target_index-orign_index);
map.get(byteArr,0,target_index-orign_index);
} catch (Exception e) {
System.out.println("读取"+orign_index+"到"+target_index+"失败");
e.printStackTrace();
}
return byteArr;
}
}
class WriteTask implements Runnable{
@Override
public void run() {
byte[] a = new byte[1024];
int cur_index;
System.out.println("执行");
while((cur_index = index.get())<size){
int target_index = (cur_index+buffer_size)>size? (int)size :cur_index+buffer_size;
if(index.compareAndSet(cur_index,target_index+1)){
//如果cas 成功就进行读写操作
//成功
System.out.println(Thread.currentThread().getName()+"写了cur_index"+cur_index+"到"+target_index+"的缓冲数据");
writeFile(cur_index,target_index,a);
// 读取
}
}
}
public void writeFile(int orign_index,int target_index,byte[] content){
//然后进行写
// 读取文件,使用一个map内存映射进行读取,可以加速读取吧
MappedByteBuffer map;
byte[] byteArr = new byte[target_index-orign_index];
try {
map = targetFileChannel.map(FileChannel.MapMode.READ_ONLY,orign_index,target_index-orign_index);
map.position();
map.put(content);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws FileNotFoundException {
File orign = new File("D:\\work_space\\project\\leetcode\\src\\test.txt");
ParalFile pf = new ParalFile(orign,30);
ThreadPoolExecutor poll = new ThreadPoolExecutor(4,5,1, TimeUnit.HOURS,new LinkedBlockingDeque<>(10) );
for(int i=0;i<5;i++){
poll.execute(pf.new ReadTask());
}
}
}
3. 大文件性能测试
补档,等