java - 防止从多个线程访问时删除文本文件
问题描述
我正在尝试调试刚刚出现在我的程序中的问题。到目前为止,我一直在使用以下代码结构毫无问题地编写、读取和更新 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
andsynchronized
修饰符setAndReplace()
并调用setAndReplace()
5,000 次,旧数据仍会保留(为什么?) - 只要 JVM 正常结束。即使我在setAndReplace()
(调用后FileOutputStream
)添加随机延迟,这似乎也是正确的。当我尝试使用
ExecutorService
(我偶尔在我的程序中访问setAndReplacePropValue()
viaExecutorService
)修改 props 文件时,只要在FileOutputStream
. 如果我添加延迟并且延迟是在 future.get() 中设置的 > 'timout' 值(因此interrupted exception
被抛出),则不会保留数据。即使我在方法中添加static
+synchronized
关键字,这仍然是正确的。
简而言之,我的问题是为什么文件被删除最可能的解释是什么?(我认为第 3 点可能会解释错误,但我实际上并没有sleeping
在调用之后,new FileOutputStream()
所以大概这不会阻止数据在调用之后被写回文件new FileOutputStream()
)。有没有我没有想到的另一种可能性?
另外,为什么第2点是真的?如果方法未声明为静态/同步,这不应该导致一个线程InputStream
从空文件创建吗?谢谢。
解决方案
不幸的是,如果没有大量附加信息,很难就您的代码提供反馈,但希望我的评论会有所帮助。
一般来说,让多个线程从同一个文件中读取和写入是一个非常糟糕的主意。我完全同意@Hovercraft-Full-Of-Eels,他建议您有1 个线程进行读/写,而其他线程只是将更新添加到 shared BlockingQueue
。
但是,这里有一些评论。
如果 setAndReplace() 声明为静态 + 同步,则保留原始道具数据。
对,这停止了代码中可怕的竞争条件,其中 2 个线程可能同时尝试写入输出文件。或者可能是 1 个线程开始写入,而另一个线程读取了一个空文件,导致数据丢失。
如果 JVM 被杀死/终止(在 FileOutputStream 被调用之后),那么之前的数据将被删除。
我不太了解这部分,但是您的代码应该有很好的 try/finally 子句,以确保在 JVM 终止时正确关闭文件。如果 JVM 被硬杀死,则文件可能已打开但尚未写入(取决于时间)。在这种情况下,我建议您写入一个临时文件并重命名为您的原子属性文件。然后,如果 JVM 被杀死,您可能会错过更新,但文件永远不会被覆盖并且为空。
如果我从 setAndReplace() 中删除静态和同步修饰符并调用 setAndReplace() 5,000 次,旧数据仍然保留(为什么?)
不知道。取决于比赛条件。也许你只是走运了。
当我尝试使用 ExecutorService 修改 props 文件时(我偶尔会在我的程序中通过 ExecutorService 访问 setAndReplacePropValue()),只要在 FileOutputStream 之后没有延迟,文件内容就会被保留。如果我添加延迟并且延迟>在 future.get() 中设置的“超时”值(因此引发中断的异常),则不会保留数据。即使我在方法中添加静态 + 同步关键字,这仍然是正确的。
如果没有看到具体的代码,我无法回答这个问题。
实际上,如果您有一个带有 1 个线程的固定线程池,那么这将是一个好主意,那么每个想要更新值的线程只需将字段/值对象提交到线程池。这大概就是@Hovercraft-Full-Of-Eels 所说的。
希望这可以帮助。
推荐阅读
- python - 如何通过电子邮件或其他字段进行 Flask-Security 登录?
- msys2 - Msys2 包含的软件包
- apache - 如何在 Apache 配置中隐藏 URL 中的查询参数?
- python - 在 Eve 的 pre-put 回调中从数据库中获取项目的信息
- angular - TS2345:“RolesGuard”类型的参数不可分配给“CanActivate”类型的参数
- autosar - adaptive AUTOSAR - difference between function group and application?
- package - Installing Racket Packages without installing Dr Racket
- elasticsearch - 如何使用映射将符号映射到弹性搜索中的单词?
- sql - Oracle 嵌套表约束
- outlook - 64 位应用程序中的 .msg 预览不起作用