首先,我在编码方面是个新手,所以我为我可能犯的任何隐语错误道歉
我正在使用Java(openJDK11)和Spring boot开发后端服务器:
该应用程序由许多面板和子面板组成,这些面板和子面板从web浏览器打开。单击子面板时,在html" target="_blank">前端执行三个不同的GET请求。
这三个请求需要不同的响应(json模式、json数据等)。每个请求启动一个访问相同配置文件的不同线程(每个子面板有一个配置文件),该配置文件将被解析。读取配置文件后,每个线程执行不同的操作,它们的共同点只有配置读取器部分。
>
有时(这让我想到并发性),其中一个/一些线程中的读取操作无法执行,因为bufferedReader。readLine()
返回null,不读取任何行。
此外,有时在正确读取某些行之后,突然bufferedReader。readLine()
返回null,但文件尚未完全读取。
每个线程创建一个本地InputStream
来打开文件,并创建一个本地BufferedReader
来解析文件。
我已经尝试使同步的parseFile
方法(虽然我觉得这是不对的,因为我不希望其他线程使用这个方法-读取其他文件-等待)。
以下是被访问和读取文件的代码片段(逐行)。这是一个“缩小”的例子。正如一些用户在下面评论的那样,异常不在这里处理,而是在真正的代码中。这只是为了显示引起麻烦的部分。
// 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个线程(每个线程为同一配置文件打开自己的InputStream
和一个BufferedReader
)工作正常
实际上,可以在不同的线程中创建InputStreams,所有线程都指向同一个文件,然后使用BufferedReader读取,而无需同步任何内容。
我的第一个错误是在这里显示原始代码的过于简化的版本。我专注于展示我所认为的问题所在。这是我的第一篇文章,下次我会做得更好。
我的代码中的错误出现在getConfig
方法中,该方法使用面板和子面板参数构建fileName
。此方法在返回此文件名
变量之前,将文件从服务器(仅当其已更改)下载到本地访问的目标/
目录。问题是文件总是被下载,所以在当前线程中读取时,它被另一个线程重新下载(覆盖)。
下面是我原本应该放在帖子中的代码:
// 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和return InputStream
对我有效的原因。
这需要在不同的地方修复问题:*我只需要在文件发生更改时下载文件(如预期的那样)*对于文件相同的情况(不使用文件名
字符串),我还将同步整个下载输入流创建
)
对我的案子起作用的是以下几点:
//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 = getInputStream(fileName);
PanelSchemaEntity schema = new PanelSchemaEntity();
return parseFile(schema, input);
}
@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 = getInputStream(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
public InputStream getInputStream(String file)
{
synchronize (file)
{
InputStream input = new FileInputStream(fileName);
}
}
// 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初始化移动到getInputStream
方法,该方法在文件相同时进行同步。
请随时更正。我很感激他们
您的问题与并发无关。这里是同时读取文件的示例。并发可能发生在同时写入和读取文件的情况下。但是对于读取文件来说,不存在数据不一致或并发的问题。
请对以下代码进行必要的更改以运行。
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();
}
}}
我的问题与这个老问题很相似,但没有令人满意的答案贴在那里。 在DB2中有一个DB表,我试图通过两个或多个单独的Java线程对其进行并行记录读取,前提是这些线程应该读取不同的数据集,即如果线程1读取了前1000条记录,线程2不应该选择这些记录,而是选择不同的1000条记录(如果可用的话)。 由于线程将读取不同的行,因此在读取时不会发生冲突。Connection对象也不会在线程之间共享--它们将使用自
我的程序如下: 输入: 提交ID名称薪资部门编号 提交ID1名称1 25100部门1 提交ID2名称2 25200部门2 提交ID3名称3 25300部门3 提交ID4名称4 25400部门4 提交ID5名称5 25500部门5 提交ID6名称6 25600部门6 提交ID7名称7 25700部门7 提交ID8名称8 25800部门8 提交ID9名称9 25900部门9 提交ID10名称10 26
我最近将应用程序从log4j更改为logback/slf4j。一切都工作得很好,但我想实现一些具体的东西。 我正在开发的应用程序是一个web应用程序。在我们的生产环境中,日志级别是on info。不时有票进来让我们的服务团队处理。如果我们的服务团队在复制票据时,他们可以将日志级别放在跟踪上,只用于他们的测试请求,那就太好了。这样,日志文件就不会随着当时所有其他请求的到来而被修改。 我们已经使用标头
问题内容: 据我所知,操作系统创建线程时,每个线程都会获得一个不同的堆栈。我想知道每个线程是否也有与自己不同的堆吗? 问题答案: 否。所有线程共享一个公共堆。 每个线程都有一个专用堆栈,它可以快速添加和删除其中的项目。这样可以使基于堆栈的内存速度更快,但是,如果您使用过多的堆栈内存(如无限递归中所发生的那样),则会导致堆栈溢出。 由于所有线程共享同一个堆,因此必须同步对分配器/释放器的访问。有许多
问题内容: 我有一个方法。值在内部被更改,我想将其返回给该方法。有没有办法做到这一点? 问题答案: 可以使用局部最终变量数组。该变量必须是非基本类型,因此可以使用数组。你还需要同步两个线程,例如使用CountDownLatch: 你也可以这样使用an Executor和a Callable:
问题内容: 我有一个如下的Java线程: 我大约有300个ID,每隔几秒钟-我启动线程以对每个ID进行呼叫。例如。 现在,我想从每个线程收集结果,并批量插入数据库,而不是每2秒进行300次数据库插入。 知道我该如何做到吗? 问题答案: 如果要在执行数据库更新之前收集所有结果,则可以使用该方法。如daveb建议的那样,如果您一次提交一项任务,则可以完成簿记工作。