首先,我在编码方面还很陌生,所以我为我可能犯的任何错误而深表歉意。

我正在使用 Java(openJDK11) Spring boot 开发后端服务器:

该应用程序由许多面板和子面板组成,这些面板和子面板可通过Web浏览器打开。单击子面板时,将在前端执行三个不同的GET请求。

这三个请求期望不同的响应(json模式,json数据等)。每个请求启动一个不同的线程,该线程访问相同的配置文件(每个子面板有一个配置文件),该文件被解析。读取配置文件后,每个线程执行不同的操作,它们仅具有相同的配置读取器部分。

  • 有时(这让我想到了并发性),无法在其中一个/某些线程中执行读取操作,因为bufferedReader.readLine()返回 null 却不读取任何行。
  • 同样,有时会发生以下情况:正确读取某些行后,突然bufferedReader.readLine()返回 null ,但是文件尚未完全读取。

  • 每个线程都会创建一个本地InputStream来打开该文件,并创建一个本地BufferedReader来对其进行解析。

    我尝试过使同步成为parseFile方法(尽管我觉得这是不对的,因为我不希望其他使用此方法的线程(读取其他文件)等待)。

    下面包括访问和读取文件(逐行)的代码段。 这是一个“reduced”示例。正如某些用户在下面评论的那样,此处未处理异常,但在实际代码中。这仅是为了显示引起故障的部分。

    
    // REST CONTROLLER
    
    @GetMapping(value = "/schema/{panel}/{subpanel}")
    public PanelSchemaEntity getSchema(String panel, String subpanel)
    {
      //Retrieves the config-file name associated to the given panel+subpanel
      String fileName = getConfig(panel, subpanel);
      // fileName = "target/config/panelABC1.txt"
    
      InputStream input = new FileInputStream(fileName);
      PanelSchemaEntity schema = new PanelSchemaEntity();
      parseFile(schema, input);
    
      return schema;
    }
    
    @GetMapping(value = "/data/{panel}/{subpanel}")
    public PanelDataEntity get(String panel, String subpanel)
    {
      //Retrieves the config-file name associated to the given panel+subpanel
      String fileName = getConfig(panel, subpanel);
      // fileName = "target/config/panelABC1.txt"
    
      InputStream input = new FileInputStream(fileName);
      PanelSchemaEntity schema = new PanelSchemaEntity();
      parseFile(schema, input);
    
      String dataFileName = getDataFile(panel, subpanel);
      // dataFileName = "target/config/panelABC1.dat"
      InputStream data = new FileInputStream(dataFileName);
    
      return new PanelDataEntity(schema, data);
    }
    
    

    // PLACED IN SOME UTILS PACKAGE
    
    // Fills the PanelSchemaEntity with the content read from input
    public PanelSchemaEntity parseFile(PanelSchemaEntity schema, InputStream input)
    {
      BufferedReader reader = new BufferedReader(new InputStreamReader(input));
      String nextLine = reader.readLine();
    
      // The data file is read and used to complete panel schema entity
      while(nextLine != null)
      {
        // Here goes the code that uses each line's content to
        // fill some schema's attributes
      }
      reader.close();
      return schema;
    }
    
    

    再次抱歉,我可能会犯任何错误,谢谢大家:)

    编辑
  • 要多次读取的文件很小,但是无法存储在缓存中,因为它与可能不会很快再次打开的面板有关。另外,有太多配置文件(每个子面板一个)无法缓存,每个文件可能会更改
  • 我非常希望不包括新的库,因为我没有这样做的权限,所以我只需要通过在代码
  • 中包含小的更改来解决此问题。
  • 同样,当调试时,3个线程(每个线程针对同一配置文件打开自己的InputStreamBufferedReader)可以完美地工作


  • 回答

    实际上,可以在不同的线程中创建InputStreams,它们都指向同一文件,然后使用BufferedReader读取它,而无需同步任何内容。

    我的第一个错误是在此处显示原始代码的简化版本。我专注于展示我认为的问题所在。这是我的第一篇文章,下次我会做的更好。

    我的代码中的错误在于getConfig方法,该方法使用panel和subpanel参数构建fileName。在返回此fileName变量之前,此方法将文件从服务器下载(仅在文件已更改的情况下)到target/目录中,以便在本地进行访问。起作用错误的原因是,文件总是被下载,因此在当前线程中读取该文件时,另一个线程正在重新下载(覆盖)该文件。

    在下面找到我本应放置在帖子中的代码:

    
    // REST CONTROLLER
    
    @GetMapping(value = "/schema/{panel}/{subpanel}")
    public PanelSchemaEntity getSchema(String panel, String subpanel)
    {
      //Retrieves the config-file name associated to the given panel+subpanel
      ConfigFile configFile = getConfig(panel, subpanel);
      // configFile.getPath() = "target/config/panelABC1.txt"
    
      InputStream input = new FileInputStream(configFile.getPath());
      PanelSchemaEntity schema = new PanelSchemaEntity();
      parseFile(schema, input);
    
      return schema;
    }
    
    @GetMapping(value = "/data/{panel}/{subpanel}")
    public PanelDataEntity get(String panel, String subpanel)
    {
      //Retrieves the config-file name associated to the given panel+subpanel
      ConfigFile configFile = getConfig(panel, subpanel);
      // configFile.getPath() = "target/config/panelABC1.txt"
    
      InputStream input = new FileInputStream(configFile.getPath());
      PanelSchemaEntity schema = new PanelSchemaEntity();
      parseFile(schema, input);
    
      String dataFileName = getDataFile(panel, subpanel);
      // dataFileName = "target/config/panelABC1.dat"
      InputStream data = new FileInputStream(dataFileName);
    
      return new PanelDataEntity(schema, data);
    }
    
    

    // PLACED IN SOME UTILS PACKAGE
    
    // Creates fileName and downloads file (if changed)
    public ConfigFile getConfig(String panel, String subpanel)
    {
      String filePathInServer = findFilePathInServer(panel, subpanel);
    
      // ERROR here: the download was happening always
      String localFilePath = donwloadIfChanged(filePathInServer);
    
      ConfigFile configFile = new ConfigFile(localFilePath);
    
      return configFile;
    }
    
    

    // PLACED IN SOME UTILS PACKAGE
    
    // Fills the PanelSchemaEntity with the content read from input
    public PanelSchemaEntity parseFile(PanelSchemaEntity schema, InputStream input)
    {
      BufferedReader reader = new BufferedReader(new InputStreamReader(input));
      String nextLine = reader.readLine();
    
      // The data file is read and used to complete panel schema entity
      while(nextLine != null)
      {
        // Here goes the code that uses each line's content to
        // fill some schema's attributes
      }
      reader.close();
      return schema;
    }
    
    

    当我在getInputStream方法中移动InputStream创建时,我还在那里下载了文件。这就是为什么同步整个getInputStream = download file + create and return InputStream对我有用的原因。

    这需要在不同位置修复问题:
    *仅当文件更改时(如预期的那样),我才需要下载文件
    *对于文件相同的情况,我还将同步整个download + InputStream creation(不使用fileName字符串)

    最佳答案

    您的问题与并发无关。请参阅此处是同时读取文件的示例。
    同时写入和读取文件时可能会发生并发。但是,对于读取文件,没有数据不一致或并发的问题。

    请对以下代码进行必要的更改才能运行。

    public static void main(String[] args) {
        String fileName = "/home/note.xml";
        FileReadThread frth1 = new FileReadThread(fileName, "ThreadOne");
        FileReadThread frth2 = new FileReadThread(fileName, "ThreadTwo");
        FileReadThread frth3 = new FileReadThread(fileName, "ThreadThree");
        frth1.start();
        frth2.start();
        frth3.start();
    }}
    
    
    
    private String fileName;
    private String threadName;
    public FileReadThread(String fileName, String threadName) {
        this.fileName = fileName;
        this.threadName = threadName;
    }
    
    @Override
    public void run() {
        InputStream input;
        try {
            input = new FileInputStream(fileName);
            BufferedReader reader = new BufferedReader(new InputStreamReader(input));
            String strCurrentLine;
            while ((strCurrentLine = reader.readLine()) != null) {
                System.out.println(threadName + "--" + strCurrentLine);
            }
            reader.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }}
    

    10-07 19:20
    查看更多