如何使用java多线程将大文本文件拆分为更小的块

时间:2013-07-29 14:59:58

标签: java multithreading file-io split

我正在尝试开发一个多线程java程序,用于将大型文本文件拆分为较小的文本文件。创建的较小文件必须具有前缀数量的行。 例如: 如果输入文件的行数是100而输入的数字是10,我的程序的结果是将输入文件分成10个文件。 我已经开发了我的程序的单线程版本:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class TextFileSingleThreaded {

    public static void main(String[] args) {
        if (args.length != 2) {
            System.out.println("Invalid Input!");
        }

        //first argument is the file path
        File file = new File(args[0]);

        //second argument is the number of lines per chunk
        //In particular the smaller files will have numLinesPerChunk lines
        int numLinesPerChunk = Integer.parseInt(args[1]);

        BufferedReader reader = null;
        PrintWriter writer = null;
        try {
            reader = new BufferedReader(new FileReader(file));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        String line;        

        long start = System.currentTimeMillis();

        try {
            line = reader.readLine();
            for (int i = 1; line != null; i++) {
                writer = new PrintWriter(new FileWriter(args[0] + "_part" + i + ".txt"));
                for (int j = 0; j < numLinesPerChunk && line != null; j++) {
                    writer.println(line);
                    line = reader.readLine();
                }
                writer.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        writer.close();

        long end = System.currentTimeMillis();

        System.out.println("Taken time[sec]:");
        System.out.println((end - start) / 1000);

    }

}

我想编写这个程序的多线程版本,但我不知道如何从指定的行开始读取文件。请帮帮我。 :(

2 个答案:

答案 0 :(得分:2)

  

我想编写这个程序的多线程版本,但我不知道如何从指定的行开始读取文件。请帮帮我。 :(

我不会,因为这暗示,每个线程从文件的开头读取,忽略行,直到它们到达输入文件的部分。这是非常低效的。正如您所暗示的那样,如果文件将按行划分为块,则读者必须阅读所有先前的行。这意味着一大堆重复的读取IO将导致应用程序变慢。

你可以改为拥有1位读者和N位作家。读者将添加要写入每个作者某种BlockingQueue的行。这个问题是你很可能不会得到任何并发性。只有一个作者最有可能同时工作,而其余的作者等待读者到达他们输入文件的一部分。此外,如果阅读器比编写器更快(很可能),那么如果要分割的文件很大,则可能很容易耗尽内存中的所有行。你可以使用一个大小有限的阻塞队列,这意味着读者可能会阻止等待编写者,但同样,多个编写者很可能不会同时运行。

正如评论中所提到的,由于这些限制,最有效的方法是单线程。如果您将此作为练习,那么听起来您需要一次读取文件,记下文件中每个输出文件的开始和结束位置,然后将线程分叉到这些位置以便它们可以重新开始 - 读取文件并将其并行写入单独的输出文件,而无需大量的行缓冲。

答案 1 :(得分:0)

您只需要读取一次文件,并将其存储到列表中:

BufferedReader br = new BufferedReader(new FileReader(new File("yourfile")));
List<String> list = new ArrayList<String>();
String line;
//for each line of your file
while((line = br.readLine()) != null){
    list.add(line);
}
br.close();

//then you can split your list into differents parts
List<List<String>> parts  = new ArrayList<ArrayList<String>>();
for(int i = 0; i < 10; i++){
  parts.add(new ArrayList<String>());
  for(int j =0; j < 10; j++){
    parts.get(i).add(list.get(i*10+j));
  }
}
//now you have 10 lists which each contain 10 lines
//you still need to to create a thread pool, where each thread put a list into a file

有关线程池的更多信息,请阅读this