首页 > 解决方案 > 将 JSON 文件读入集合,最佳实践

问题描述

我正在开发一个 JavaFX 应用程序。我有几个 JSON 文件,我想读取这些文件并将其插入到域对象的集合中。我目前正在使用 Gson 来读取这些文件。我的应用程序目前正在运行,但是在应用程序启动之前有很长的延迟。我认为这是因为我在同一个线程中按顺序读取这些文件。因此,我希望通过引入一些并发来缩短启动时间。我在想如果我能弄清楚如何并行读取文件,它应该会加快启动时间。我对并发的想法很陌生,所以我正在努力学习。不用说,我遇到了一些障碍,似乎无法在网上找到太多信息或示例。

这是我的问题:

  1. 不确定 JSON 文件读取是否可以在后台线程中完成。

域类使用这些集合来计算并最终在 GUI 中显示值。据我了解,如果您修改 GUI,则必须在 JavaFX 应用程序线程中完成,而不是在后台完成。我不确定加载要在 GUI 中使用的数据是否算作修改 GUI。我不会通过读取 Json 来直接更新任何 GUI 节点,例如 textField.setText("something"),所以我假设不,我不是。我错了吗?

  1. Task> 和 Thread 或 ExecutorService 和 Callable> 有什么区别?一种方法优于另一种方法吗?我都试过了,都失败了。当我尝试使用任务和后台线程时,我会收到 NullPointerException,因为应用程序在读取文件并使用数据初始化之前尝试访问集合。它从太慢变成了太快。SMH。

  2. 为了解决这个问题,我听说了 Preloaders。这里的想法是启动某种闪屏来延迟资源加载(读取 JSON 文件)完成,然后继续主应用程序。但是,这里的示例或信息非常稀缺。我正在使用 Java 10 和 IntelliJ,所以我可能已经把自己逼入了百万分之一的利基市场。

我不要求任何人为我解决我的问题。我只是有点迷路,不知道在哪里或如何进行。如果需要,我很乐意分享细节,但我认为我的问题目前仍然是概念性的。

帮助我 StackOverflow 你是我唯一的希望。

编辑:代码示例:

public class Employee {

  private List<Employee> employeeList;

  public Employee() {
    employeeList = new ArrayList<>();
    populateEmployees();
  }

  private final void populateEmployees() {
    Task<Void> readEmployees = new Task<>() {
      @Override
      protected Void call() throws Exception {
        System.out.println("Starting to read employee.json"); // #1
        InputStream in = getClass().getResourceAsStream("/json/employee.json");
        Reader reader = new InputStreamReader(in);
        Type type = new TypeToken<List<Employee>>(){}.getType();
        Gson gson = new Gson();
        employeeList.addAll(gson.fromJson(reader, type));
        System.out.println("employeeList has " + employeeList.size() + " elements"); // #2
        return null;
      }
    };
    readEmployees.run();
    System.out.println(readEmployees.getMessage());  // #3
  }
}

我看到#1 打印到控制台,从来没有#2 或3。我怎么知道它是通过任务处理的?

标签: javamultithreadingjavafxconcurrencyjavafx-8

解决方案


您的应用程序将加速多少取决于这些文件有多大以及有多少文件。您应该知道创建线程也是一项资源消耗任务。我可以想象您有大量文件并且为每个文件创建一个新线程的情况,这甚至可能使您的应用程序初始化速度变慢。如果文件量大或文件数会随时间变化,可以安排一些固定数量的线程池,例如。5 可以同时进行文件读取任务。回到问题,问题是否值得使用单独的线程来读取文件,我会说是的,但前提是您的应用程序有一些初始化工作,可以在不知道这些文件内容的情况下完成。您应该知道,在某个时间点,您可能需要等待文件解析结果。

作为解决问题的一部分,您可以做一些基准测试来检查解析每个文件进程需要多长时间,然后您就会知道什么配置/工作线程数量是最好的。例如。当解析需要 1 秒时,您不会为每个文件创建线程,但如果您有 100 个处理时间为 1 秒的文件,您可以创建一些线程池并平均分配每个线程的作业。

  1. 是的

  2. 我不知道 JavaFX,但总的来说 Thread 和 Task 的概念是相同的。线程让您确定您正在启动新线程,它是较低级别的抽象。任务是某种更高的抽象,您希望在其中单独运行部分代码,并且异步运行,但您不想知道它将在哪个线程上运行。Task 背后的一些编程语言实际上隐藏了一些线程池。

  3. 预加载器很好,因为它们向用户显示一些工作正在后台完成,因此如果应用程序已冻结,他不会担心。另一方面,如果您可以加快初始化过程,那就太好了。您可以加入这两个想法,但请记住,没有人愿意等待很多 :)


推荐阅读