多线程使java大文件处理变得更糟糕

多线程使java大文件处理变得更糟糕,java,multithreading,Java,Multithreading,我正在用java测试一个大文件(10.000.100行)的处理 我编写了一段代码,从文件中读取并生成指定数量的线程(最多等于CPU的内核),然后将文件行的内容打印到标准输出 Main类如下所示: public class Main { public static void main(String[] args) { int maxThread; ArrayList<String> linesForWorker = new ArrayLi

我正在用java测试一个大文件(10.000.100行)的处理

我编写了一段代码,从文件中读取并生成指定数量的线程(最多等于CPU的内核),然后将文件行的内容打印到标准输出

Main
类如下所示:

public class Main
{
    public static void main(String[] args)
    {
        int maxThread;
        ArrayList<String> linesForWorker = new ArrayList<String>();


        if ("MAX".equals(args[1]))
            maxThread = Runtime.getRuntime().availableProcessors();
        else
            maxThread = Integer.parseInt(args[1]);

        ExecutorService executor = Executors.newFixedThreadPool(maxThread);

        String readLine;
        Thread.sleep(1000L);

        long startTime = System.nanoTime();

        BufferedReader br = new BufferedReader(new FileReader(args[0]));


        do
        {
            readLine= br.readLine();

            if ("X".equals(readLine))
            {
                executor.execute(new WorkerThread((ArrayList) linesForWorker.clone()));

                linesForWorker.clear(); // Wrote to avoid storing a list with ALL the lines of the file in memory
             }
             else
             {
                 linesForWorker.add(readLine);
             }

        }
        while (readLine!= null);

       executor.shutdown();
       br.close();

       if (executor.awaitTermination(1L, TimeUnit.HOURS))
           System.out.println("END\n\n");

      long endTime = System.nanoTime();

      long durationInNano = endTime - startTime;

      System.out.println("Duration in hours:" + TimeUnit.NANOSECONDS.toHours(durationInNano));
      System.out.println("Duration in minutes:" + TimeUnit.NANOSECONDS.toMinutes(durationInNano));
      System.out.println("Duration in seconds:" + TimeUnit.NANOSECONDS.toSeconds(durationInNano));
      System.out.println("Duration in milliseconds:" + TimeUnit.NANOSECONDS.toMillis(durationInNano));

    }
}
class WorkerThread implements Runnable
{
    private List<String> linesToPrint;

    public WorkerThread(List<String> linesToPrint) { this.linesToPrint = linesToPrint; }

    public void run()
    {
        for (String lineToPrint : this.linesToPrint)
        {
          System.out.println(String.valueOf(Thread.currentThread().getName()) + ": " + lineToPrint);
        }

        this.linesToPrint = null; // Wrote to help garbage collector know I don't need the object anymore
    }
}
我运行的应用程序指定1和“MAX”(即CPU核心的数量,在我的情况下是4)作为FixedThreadPool的最大线程,我经历了:

  • 使用
    FixedThreadPool
    中的一个单线程执行应用程序时,执行时间约为40分钟
  • 使用
    FixedThreadPool
    中的4个线程执行应用程序时,执行时间约为44分钟
有人能解释我这种奇怪(至少对我来说)的行为吗?为什么多线程在这里没有帮助

另外,我的机器上有SSD

编辑:我修改了代码,以便线程现在创建一个文件,并将其行集写入SSD中的该文件。现在执行时间已经减少到5秒左右,但我仍然知道程序的单线程版本运行时间约为5292毫秒,而多线程(4线程)版本运行时间约为5773毫秒


为什么多线程版本仍然更持久?可能每个线程,即使是要写入其“个人”文件,也必须等待其他线程释放SSD资源,以便访问和写入它?

您使用一个线程读取文件行。从HDD或SSD的多个线程读取文件内容对您没有好处(HDD会崩溃,性能会更差),所以这没关系
System.out.println
是一个同步函数,它可能会极大地限制您的处理线程。我们说的是多少行?40分钟似乎是极端的。您可能受到IO限制,或者受到有关
System.out.println
的争论的困扰。请注意,克隆您的ArrayList是不必要的开销。只需按原样传递ArrayList,并指定ArrayList的新实例,而不是清除旧实例。您还可以创建比默认值16大的ArrayList。您发布的代码中唯一的多线程部分是文件内容的打印,甚至不是真正的多线程,因为您没有同时启动线程。可能是一个给定的线程在下一个线程开始之前就完成了。在这种情况下,启动额外线程的开销只会增加代码执行时间!这些文件包含10.000.100行,正如我在问题开头所写的,我建议您缓冲控制台输出(描述了一种简单的方法,但我会在内存中使用某种线程安全列表)在写入
System.out
之前,请使用另一种方式使用适当的日志框架记录数据,例如支持异步日志记录。如果你的代码真的是你发布的1:1,它就不应该像你描述的那样长(只有当你在一些非常旧的机器上运行或者有一个坏掉的ssd时)。我的建议是:不要在生产中使用
System.out.println
,您只需一个线程即可读取文件行。从HDD或SSD的多个线程读取文件内容对您没有好处(HDD会崩溃,性能会更差),所以这没关系
System.out.println
是一个同步函数,它可能会极大地限制您的处理线程。我们说的是多少行?40分钟似乎是极端的。您可能受到IO限制,或者受到有关
System.out.println
的争论的困扰。请注意,克隆您的ArrayList是不必要的开销。只需按原样传递ArrayList,并指定ArrayList的新实例,而不是清除旧实例。您还可以创建比默认值16大的ArrayList。您发布的代码中唯一的多线程部分是文件内容的打印,甚至不是真正的多线程,因为您没有同时启动线程。可能是一个给定的线程在下一个线程开始之前就完成了。在这种情况下,启动额外线程的开销只会增加代码执行时间!这些文件包含10.000.100行,正如我在问题开头所写的,我建议您缓冲控制台输出(描述了一种简单的方法,但我会在内存中使用某种线程安全列表)在写入
System.out
之前,请使用另一种方式使用适当的日志框架记录数据,例如支持异步日志记录。如果你的代码真的是你发布的1:1,它就不应该像你描述的那样长(只有当你在一些非常旧的机器上运行或者有一个坏掉的ssd时)。我的建议是:不要在生产中使用
System.out.println