【问题标题】:Reading multiple text files in different threads in Java在Java中的不同线程中读取多个文本文件
【发布时间】:2021-02-15 23:13:28
【问题描述】:

我正在学习多线程,并希望使用不同的线程同时读取不同线程中的多个文本文件并在单个列表中获得结果。我有包含员工名字和姓氏的文本文件。

我写了下面的Employee类。

class Employee {
    String first_name;
    String last_name;
    public Employee(String first_name, String last_name) {
        super();
        this.first_name = first_name;
        this.last_name = last_name;
    }
}

读取文件的类,用List来存储对象。

class FileReading {
    List<Employee> employees = new ArrayList<Employee>();
    public synchronized void readFile(String fileName) {
        try {
            FileReader fr = new FileReader(new File(fileName));
            BufferedReader br = new BufferedReader(fr);
            String line;
            while ((line = br.readLine()) != null) {
                String[] arr = line.split("\\s+");
                employees.add(new Employee(arr[0], arr[1]));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

带有主方法和线程的类。

public class TestMultithreading {

    public static void main(String[] args) {

        final FileReading fr = new FileReading();

        Thread t1 = new Thread() {
            public synchronized void run() {
                fr.readFile("file1.txt");
            }
        };

        Thread t2 = new Thread() {
            public synchronized void run() {
                fr.readFile("file2.txt");
            }
        };

        Thread t3 = new Thread() {
            public synchronized void run() {
                fr.readFile("file3.txt");
            }
        };

        t1.start();
        t2.start();
        t3.start();

        try {
            t1.join();
            t2.join();
            t3.join();
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }

        System.out.println(fr.employees.size());
    }
}

使用 join() 方法是否确保完成调用它的线程并继续执行另一个线程?如果是,多线程的意义何在? 有没有其他方法可以确保所有线程并行运行并在它们都在 main() 方法中完成后从它们那里收集结果?

【问题讨论】:

    标签: java multithreading


    【解决方案1】:

    所有线程并行运行,但是您的readFile 方法是同步的,因此任何时候只有一个线程可以进入它(每个对象)。这是一个不错的选择,因为它可以防止同时更新ArrayList(它不是线程安全的),但也意味着任何时候两个线程都会在进入readFile 方法之前等待。

    如果您创建三个FileReading 实例,您的代码将并行运行。

    join() 方法执行另一种同步:它阻塞调用线程,直到另一个线程的run() 方法退出。因此,您可以确定在代码中的三个 joins 之后,三个线程已经完成。

    【讨论】:

      【解决方案2】:

      另一个答案非常清楚。参考它以了解如何更好地解决您的问题。我会给你一个食谱以及一些解释。

      首先,您不需要 FileReading 类(BTW 抽象不好)。仅Runnable 实例(例如匿名类)接收要从中读取数据的文件名和目标列表。

      您将这些 Runnables 传递给 Thread 实例构造函数并将它们保存在某个列表或集合中,因此您可以对它们中的每一个调用 thread.start()(即使用 set.forEach())并对 thread.join() 执行相同的操作。在同步的块或方法中不需要做任何事情。

      这样,您的 main 方法将等待所有这些线程完成,同时仍利用并行性(会有一些等待较慢的文件完成,但所有线程仍将并行执行繁重的工作 - 在至少在文件系统/存储允许的范围内)。

      您所说的join() 是正确的,但线程在连接之前并行工作的可能性也是正确的。关键是只有在每个任务结束后才会发生连接。因此,花费较少时间的文件任务将全部并行完成。较慢的任务仍然会整体利用并行性,而主要方法正在等待结束的较快的任务,下一个将是较慢的任务,直到所有任务都结束并且最终允许主要方法继续。

      这就像烤蛋糕一样,您可以在一段时间内并行执行任务,但所有任务都必须加入到最后进入烤箱的单个收件人中。

      其次,最好创建一个原子可插入的 List(例如参见 Collections.synchronizedList(new ArrayList&lt;&gt;()) 或更现代的语法,这样您就可以将它传递给 Runnables 并让它们同时填充它,同时仍然防止运行条件。这是需要同步代码的地方,它已经在内部创建的列表中提供。

      最后,我认为您不应该为每个文件创建一个编号为 Thread 的引用,thread1thread2 等。您应该有一个文件列表并按需创建 Threads在遍历它时,然后将Threads 存储在提到的集合或列表中,以便稍后一次性引用它们。

      【讨论】:

      • 如何创建 Runnable 实例(例如匿名类)来接收要从中读取数据的文件名和目标列表?
      • @Nagaraja 没有考虑到这一点,但是如果无法通过构造函数传递它,您可以在声明/实例化匿名类时从外部初始化公共实例变量。要创建一个匿名类实例,例如 Runnable myRunnable = new Runnable() { @Override public void run() { ... } };
      • @NagarajaJB 这就是它被称为匿名的原因,当您使用这种语法时,您只是在声明要实例化一个实现Runnable 接口并提供类似这样的实现的类因此,实际上不必声明一个常用的 Java 类并为其命名,然后调用new MyClass();。顺便说一句,Java lambda 最初被简单地实现为 syntatic sugar to anonymous classes(这已经很久了)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-01
      • 2010-11-28
      • 1970-01-01
      • 1970-01-01
      • 2017-01-29
      相关资源
      最近更新 更多