1.问题:使用单线程处理数G的文件时,压力较大
单线程的文件处理一般适用于小文件,而数G的大文件一般便不再适用单线程处理,于是我们采用多线程对其进行处理,提高效率.
2.算法思路与步骤
思路:将大文件分为8段,然后使用8个线程并发处理.
步骤:
(1)创建一个与原文件等大的空白文件,用作容器.因为多线程的执行顺序时不可预期的,所以不能一段一段的拼接,而是需要一个完成的容器来存放数据
(2)为每条线程创建一个单独的随机访问文件流对象,防止多线程出现的共享资源问题
(3)将原文件分为8段,再创建8条线程分别处理一段.(一般都不能完全均分,所以剩下的数据交给最后一个创建的线程处理)
(4)创建一个线程进行测速和进度提示
代码实现
主类:
package MulThreadFileCopy;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Timer;
import java.util.TimerTask;
public class Copy {
public static long len,segmentLen;
public static RandomAccessFile[] raf_source = new RandomAccessFile[8];//创建8个IO口一起读文件
public static RandomAccessFile[] raf_target = new RandomAccessFile[8];//创建8个IO口一起写文件
public static long processedCnt = 0;//已处理字节数
public static boolean[] flag = new boolean[8];//记录每个线程的处理进度
public static void main(String[] args) throws IOException {
for (int i = 0; i < 8; i++) {
//初始化
raf_source[i] = new RandomAccessFile("D:\\迅雷下载\\蝙蝠侠:黑暗骑士.BD1280高清中英双字版.mp4","rwd");
raf_target[i]= new RandomAccessFile("D:\\迅雷下载\\复制版本.mp4","rwd");
}
len = raf_source[0].length();//获取文件长度
raf_target[0].setLength(len);//创建一个新的等大空白文件,方便进行多线程操作
segmentLen = len / 8;//获取每一段的长度
for (int i = 0; i < 8; i++) {
//开八条线程,并通过构造方法传参,通知各线程所要处理的数据段
new ProData(i).start();
}
new SpeedGetter().start();//开启计速线程
}
}
线程类:
package MulThreadFileCopy;
import java.io.IOException;
public class ProData extends Thread{
private int tid;//线程id
@Override
public void run() {
long begin = tid * Copy.segmentLen;//计算开头位置
long end = (tid==7) ? Copy.len : begin + Copy.segmentLen;//计算末尾位置
byte[] buffer = new byte[1024*1024];
int validLen;
for (long i = begin;i < end;i += 1024*1024){//一次处理1M的数据
try {
Copy.raf_source[tid].seek(i);//设置读写位置
validLen = Copy.raf_source[tid].read(buffer);//读
Copy.raf_target[tid].seek(i);//设置读写位置
Copy.raf_target[tid].write(buffer,0,validLen);//写
Copy.processedCnt += 1024*1024;//更新已经处理的字节数
} catch (IOException e) {
e.printStackTrace();
}
}
try {
Copy.raf_target[tid].close();//关闭随机访问文件流,
Copy.raf_source[tid].close();
} catch (IOException e) {
e.printStackTrace();
}
Copy.flag[tid]=true;//标志该线程处理结束
}
public ProData(int id) {
this.tid=id;
}
}
计速类
package MulThreadFileCopy;
public class SpeedGetter extends Thread{
private int sec = 0;//已经经过的秒数
private boolean check;//检查是否处理完毕
@Override
public void run() {
while (true){
sec++;
check = false;
System.out.println(((double) Copy.processedCnt/1024/1024/sec)+"MB/S");
System.out.println(((double) Copy.processedCnt/Copy.len*100)+"%");
for (int i = 0; i < 8; i++) {
if(!Copy.flag[i]){
check=true;
}
}
if(!check) {
System.out.println("已经处理完成!");
System.exit(0);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行效果:
如代码里所见,笔者这里使用的是一部1.6G电影,而复制的速度可高达700MB/S