【发布时间】:2019-08-16 14:26:46
【问题描述】:
在我们的应用程序中,有一段代码会持续读取和调整文件。只是为了让您了解正在发生的事情:
public void run() {
try {
while(true) { //Yeah, I know...
Path currentFileName = getNextFile();
String string = readFile(currentFileName);
Files.deleteFile(currentFileName);
string = string.replaceAll("Hello", "Blarg");
writeFile(currentFileName);
}
} catch (Exception e) {
System.err.println("It's all ogre now.");
e.printStackTrace(System.err);
}
}
我们代码中的其他地方有一个方法,它可能——但通常——不会在与上述代码相同的线程上运行,我们用它来退出应用程序。
private void shutdown() {
if(fileReader != null)
fileReader = null;
System.exit(0); //Don't blame me, I didn't write this code
}
很明显,这段代码中存在潜在的竞争条件,如果在检索文件和写回文件之间调用shutdown(),则可能会导致文件完全丢失。这显然是不受欢迎的行为。
此代码存在一千个问题(超出了我在此处显示的范围),但我需要解决的主要问题是处理文件的不良行为可能在中途被切断而没有追索权。我提出的解决方案包括简单地将 while 循环包装在 synchronized 块中,并在 shutdown 中的 System.exit 调用周围放置一个块。
所以我修改后的代码如下所示:
private Object monitor = new Object();
public void run() {
try {
while(true) {
synchronized(monitor) {
Path currentFileName = getNextFile();
String string = readFile(currentFileName);
Files.deleteFile(currentFileName);
string = string.replaceAll("Hello", "Blarg");
writeFile(currentFileName);
}
}
} catch (Exception e) {
System.err.println("It's all ogre now.");
e.printStackTrace(System.err);
}
}
private void shutdown() {
synchronized(monitor) {
if(fileReader != null)
fileReader = null;
System.exit(0);
}
}
我最担心的是System.exit(0); 通话,我不确定通话幕后的整体行为。是否存在System.exit 的副作用会释放monitor 上的锁定的风险,从而有可能在System.exit 导致JVM 停止之前部分执行run 中的循环内容?或者这段代码会保证执行不会在处理单个文件的过程中尝试关闭?
注意:在一些闲散的程序员介入替代方案之前,我想指出,我在这里放的是大约 4000 行代码的截断版本,所有代码都隐藏在一个类中。是的,这太可怕了。是的,这让我后悔自己选择的职业。我不是在这里寻找这个问题的替代解决方案,我只是想确定这个特定的解决方案是否有效,或者是否有一些严重的缺陷会阻止它按我的预期工作。
【问题讨论】:
-
如何在
shutdown中设置一个布尔值并在run中检查它以查看JVM 是否即将退出? -
多线程做读/写的事情吗?
-
(另外:在
System.exit之前将变量设置为null 可能是不必要的)。 -
@GhostCat 我没有接受答案,因为没有一个答案试图证明他们的主张,比如引用文档或实施细节。我不愿意接受这样的问题的答案,如果仅仅运行代码并不能证明解决方案的合法性,如果答案只声称“这会起作用!”没有证实这一说法。
标签: java synchronization