首先,我在编码方面还很陌生,所以我为我可能犯的任何错误而深表歉意。
我正在使用 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;
}
再次抱歉,我可能会犯任何错误,谢谢大家:)
编辑
InputStream
和BufferedReader
)可以完美地工作回答
实际上,可以在不同的线程中创建
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();
}
}}