我正在尝试调试程序中刚刚出现的问题。到目前为止,我一直在使用以下代码结构编写,读取和更新props文件,而没有任何问题:
public void setAndReplacePropValue(String dir, String key, String value) throws FileNotFoundException, IOException {
if (value != null) {
File file = new File(dir);
if (!file.exists()) {
System.out.println("File: " + dir + " is not present. Attempting to create new file now..");
new FilesAndFolders().createTextFileWithDirsIfNotPresent(dir);
}
if (file.exists()) {
try {
FileInputStream fileInputStream = null;
fileInputStream = new FileInputStream(file);
if (fileInputStream != null) {
Properties properties = new Properties();
properties.load(fileInputStream);
fileInputStream.close();
if (properties != null) {
FileOutputStream fileOutputStream = new FileOutputStream(file);
properties.setProperty(key, value);
properties.store(fileOutputStream, null);
fileOutputStream.close();
}
}
}
catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println("File: " + dir + " does not exist and attempt to create new file failed");
}
}
}
但是,最近我注意到从多个线程中进行更新后,正在删除特定文件(简称为C:\\Users\\Admin\\Desktop\\props.txt
)。我不确定此错误的确切来源,因为它似乎是随机发生的。
我认为,也许,如果有两个线程调用setAndReplacePropValue()
并且第一个线程调用FileOutputStream fileOutputStream = new FileOutputStream(file);
才有机会(通过properties.store(fileOutputStream, null)
)将数据重新写入文件,则第二个线程可能会在一个空文件上调用fileInputStream = new FileInputStream(file);
-导致该线程在将“空”数据写回到文件时删除先前的数据。
为检验我的假设,我尝试从多个线程连续调用数百次至数千次setAndReplacePropValue()
,同时根据需要对setAndReplacePropValue()
进行更改。这是我的结果:
如果将setAndReplace()
声明为static
+ synchronized
,则会保留原始props
数据。即使我在调用FileOutputStream
之后添加了随机延迟,情况仍然如此-只要JVM正常存在。如果JVM被杀死/终止(在调用FileOutputStream
之后),则先前的数据将被删除。
如果我同时从static
中删除synchronized
和setAndReplace()
修饰符并调用setAndReplace()
5,000次,则旧数据仍会保留(为什么? )-只要JVM正常结束。即使我在setAndReplace()
中添加了随机延迟(在调用FileOutputStream
之后),这似乎也是正确的。
当我尝试使用ExecutorService
修改props文件(我偶尔会通过程序中的setAndReplacePropValue()
访问ExecutorService
)时,只要{ {1}}。如果添加延迟,并且延迟>在future.get()中设置的'timout'值(因此引发FileOutputStream
),则将不保留数据。即使我将interrupted exception
+ static
关键字添加到方法中,也是如此。
简而言之,我的问题是为什么文件被删除的最可能解释是什么? (我认为第3点可能会解释错误,但是在调用synchronized
之后我实际上不是sleeping
,所以大概这不会阻止在调用new FileOutputStream()
之后将数据写回到文件中)。 还有我没想到的另一种可能性吗?
还有,为什么第2点是正确的?如果未将method声明为static / synchronized,这是否不应该导致一个线程从空文件创建new FileOutputStream()
?谢谢。
答案 0 :(得分:0)
不幸的是,在没有大量其他信息的情况下,很难对您的代码提供反馈,但是希望我的评论会有所帮助。
通常,让多个线程从同一文件读取和写入是一个非常糟糕的主意。我完全同意@ Hovercraft-Full-Of-Eels,他建议您使用 1 线程进行读/写操作,而其他线程则仅将更新添加到共享的BlockingQueue
中。
但这就是这里的一些评论。
如果将setAndReplace()声明为静态+同步,则将保留原始道具数据。
正确,这将停止代码中可怕的争用条件,在该条件下,两个线程可能试图同时写入输出文件。或者可能是一个线程开始进行写入,而另一个线程读取了一个空文件,导致数据丢失。
如果JVM被杀死/终止(在FileOutputStream被调用之后),那么先前的数据将被删除。
我不太理解这部分,但是您的代码应具有良好的try / finally子句,以确保在JVM终止时适当地关闭文件。如果JVM很难使用,则文件可能已打开但尚未写入(取决于时间)。在这种情况下,我建议您写入一个临时文件,并重命名到您的原子属性文件中。然后,如果JVM被杀死,但是文件永远不会被覆盖并且为空,您可能会错过更新。
如果我同时从setAndReplace()中删除了静态修饰符和同步修饰符,并调用setAndReplace()5,000次,则旧数据仍然会保留(为什么?)
不知道。取决于比赛条件。也许你只是幸运。
当我尝试使用ExecutorService修改props文件时(我偶尔会通过程序中的ExecutorService访问setAndReplacePropValue()),只要FileOutputStream之后没有延迟,文件内容就会被保留。如果我添加了延迟,并且延迟是在future.get()中设置的> timeout值(因此抛出了中断的异常),则不会保留数据。即使我在方法中添加了静态+同步关键字,情况仍然如此。
如果不查看特定代码,我无法回答。
实际上,如果您有一个带有1个线程的固定线程池,那么这将是一个好主意,然后每个要更新值的线程都将字段/值对象提交给线程池。这大约是@ Hovercraft-Full-Of-Eels所谈论的。
希望这会有所帮助。