问题描述
给定一个txt文件,利用不同个数的线程查找文件中某字符的个数,探究线程个数与查找时间的关系。
本作业代码使用JAVA实现,版本为10.0.2,使用的IDE为Eclipse4.9.0. 结果测试所用的txt文件内容为英文,编码格式为UTF-8。
源代码
第一版代码:(仅支持单线程、按行读取、可以读取字符串/字符,速度快)
package searchtxt; //包名称
import java.io.BufferedReader; //缓冲字符输入流
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
/* *
* 读取一txt文档,每次读取一行,用BufferedReader(FileReader fr)
* */
public class demo {
static int totalCount; //待查找关键字的个数
static String key = "a"; //带查找关键字字符串
public static void main(String[] args) throws IOException {
Thread1 mTh1=new Thread1(); //创建一个线程
mTh1.setTotalCount(0); //传参,关键字个数初始化为0
mTh1.setKey(key); //传入要查找的关键字
mTh1.start(); //开启线程,运行run方法
totalCount=mTh1.getTotalCount(); //获取该线程查找结果
}
}
class Thread1 extends Thread{ //继承自Thread类
private int totalCount; //关键字个数
private String key; //关键字字符串
@SuppressWarnings("resource")
public void run() {
File f = new File("src/OneHundredYearsofSolitude.txt"); //待查找文件路径
FileReader fr; //该类按字符读取流中数据
String str;
try {
long startTime=System.currentTimeMillis(); //获取开始时间
fr = new FileReader(f);
BufferedReader br = new BufferedReader(fr);
//开始读取文件直到末尾
while ((str = br.readLine()) != null) {
//将每次读取的数据放入str字符串中,在其中查找关键字key的个数加入totalcount
setTotalCount(getTotalCount() + countKey(str, key));
}
long endTime=System.currentTimeMillis(); //获取结束时间
//输出结果
System.out.println("文章中一共出现了:" + key + ":" + totalCount + "次");
System.out.println("程序运行时间: "+(endTime-startTime)+"ms");
} catch (IOException e1) {
e1.printStackTrace();
}
}
//该方法从str中查找key,返回个数
public static int countKey(String str, String key){
int index = 0;
int count = 0;
while ((index = str.indexOf(key, index)) != -1) {
index += key.length();
count++;
}
return count;
}
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public void setKey(String key) {
this.key = key;
}
}
第二版代码:(可自行选择总线程个数,将文件分块让各个线程按字符查找)
1、MultiReadTest.java(主程序)
package searchtxt;
import java.io.File;
import java.io.RandomAccessFile; //用于读写文件
import java.util.concurrent.CountDownLatch; //CountDownLatch类,用于线程同步
/* *
* 用n个线程读取txt文件,当获取到指定关键字时,在指定的对象加1
* */
public class MultiReadTest {
@SuppressWarnings("resource")
public static void main(String[] args) {
//开始时间设为0
long startTime=0;
//结束时间设为0
long endTime=0;
/*
//可手动输入线程数目,调试时注释掉
Scanner input= new Scanner(System.in); //为Scanner实例化对象input
int n=input.nextInt(); //扫描控制台输入
final int DOWN_THREAD_NUM = n;
*/
//
//指定线程数目
//final成员变量必须在声明的时候初始化或在构造方法中初始化,不能再次赋值。
final int DOWN_THREAD_NUM = 8;
//
//要读取的txt文件路径
final String OUT_FILE_NAME = "src/8MB.txt";
//要查找的关键字
final String keywords = "a";
//CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。
//具体使用方法为:
//CountDownLatch的构造函数接收一个int类型的参数作为计数器,如果你想等待N个点完成,这里就传入N。
//当我们调用一次CountDownLatch的countDown方法时,N就会减1,CountDownLatch的await会阻塞当前线程,直到N变成零。
//在这里,我们设置CountDownLatch的值为DOWN_THREAD_NUM
CountDownLatch doneSignal = new CountDownLatch(DOWN_THREAD_NUM);
//RandomAccessFile是Java输入/输出流体系中功能最丰富的文件内容访问类,可以读取文件内容,也可以向文件输出数据
//与普通的输入/输出流不同的是,RandomAccessFile支持跳到文件任意位置读写数据
//RandomAccessFile对象包含一个记录指针,用以标识当前读写处的位置
//当程序创建一个新的RandomAccessFile对象时,该对象的文件记录指针对于文件头(也就是0处)
//当读写n个字节后,文件记录指针将会向后移动n个字节
//除此之外,RandomAccessFile可以自由移动该记录指针
RandomAccessFile[] outArr = new RandomAccessFile[DOWN_THREAD_NUM];
try{
//此方法用于获取文件长度,最大只能获取2g的文件大小,因为返回值类型为long
long length = new File(OUT_FILE_NAME).length();
//输出文件长度
System.out.println("文件总长度:"+length+"字节,即"+length/1024/1024+"MB");
//计算每个线程应该读取的字节数
long numPerThred = length / DOWN_THREAD_NUM;
System.out.println("共有"+DOWN_THREAD_NUM+"个线程,每个线程读取的字节数:"+numPerThred+"字节");
//计算整个文件整除后剩下的余数
long left = length % DOWN_THREAD_NUM;
//获取开始时间
startTime=System.currentTimeMillis();
//为每个线程打开一个输入流、一个RandomAccessFile对象
//让每个线程分别负责读取文件的不同部分
for (int i = 0; i < DOWN_THREAD_NUM; i++) {
//rw:以读取、写入方式打开指定文件
outArr[i] = new RandomAccessFile(OUT_FILE_NAME, "rw");
//最后一个线程读取指定numPerThred+left个字节
if (i == DOWN_THREAD_NUM - 1) {
//输出其要读的字节范围(测试时应把这句注释掉,因为会影响运行时间的测定)
//System.out.println("第"+i+"个线程读取从"+i * numPerThred+"到"+((i + 1) * numPerThre