【发布时间】:2016-01-06 22:37:14
【问题描述】:
我有一个在 java JDK 1.7 下运行的批处理。它在带有 RHEL、2.6.18-308.el5 #1 SMP 的系统上运行。
此过程从数据库中获取元数据对象列表。从该元数据中,它提取文件的路径。此文件可能实际存在也可能不存在。
进程使用 ExecutorService (Executors.newFixedThreadPool()) 启动多个线程。每个线程都运行一个 Callable,它启动一个进程,该进程读取该文件并在该输入文件存在时写入另一个文件(并记录结果),如果该文件不存在则不执行任何操作(记录该结果除外)。
我发现行为是不确定的。尽管每个文件的实际存在始终保持不变,但运行此过程并不能给出一致的结果。它通常会给出正确的结果,但偶尔会发现一些确实存在的文件不存在。如果我再次运行相同的进程,它会发现它之前说的文件不存在。
为什么会发生这种情况,有没有更可靠的替代方法?在其他线程尝试读取目录时在多线程进程中写入文件是错误的吗?较小的线程池会有所帮助(目前为 30 个)吗?
更新: 下面是这个场景中工作线程调用的unix进程的实际代码:
public int convertOutputFile(String inputFile, String outputFile)
throws IOException
{
List<String> args = new LinkedList<String>();
args.add("sox");
args.add(inputFile);
args.add(outputFile);
args.addAll(2, this.outputArguments);
args.addAll(1, this.inputArguments);
long pStart = System.currentTimeMillis();
int status = -1;
Process soxProcess = new ProcessBuilder(args).start();
try {
// if we don't wait for the process to complete, player won't
// find the converted file.
status = soxProcess.waitFor();
if (status == 0) {
logger.debug(String.format("SoX conversion process took %d ms.",
System.currentTimeMillis() - pStart));
} else {
logger.error("SoX conversion process returned an error status of " + status);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return status;
}
更新 #2:
我尝试过从 java.io.File.exists() 切换到 java.nio.Files.exists() 的实验,这似乎提供了更高的可靠性。我还没有看到多次尝试失败的情况,和以前一样,它发生的概率约为 10%。所以我想我想知道 nio 版本在处理底层文件系统的方式上是否更健壮。 这一发现后来被证明是错误的。 nio 在这里没有帮助。
更新 #3: 经过进一步审查,我仍然发现发生相同的故障情况。所以切换到nio并不是万能的。通过将执行程序服务的线程池大小减少到 1,我获得了更好的结果。这似乎更可靠,并且没有机会一个线程读取目录,而另一个线程正在启动一个写入相同的进程目录。
我尚未调查的另一种可能性是将输出文件放在与输入文件不同的目录中是否会更好。我将它们放在同一个目录中是因为它更容易编码,但这可能会造成混淆,因为输出文件的创建会影响与输入目录扫描相同的目录。
更新 #4: 重新编码以便将输出文件写入与输入文件(正在检查其存在)不同的目录并没有特别帮助。 唯一有帮助的改变是 ExecutorService 线程池大小为 1,换句话说,不是多线程这个操作。
【问题讨论】:
-
你的问题很不清楚。请问可以发MCVE吗?
-
我无法轻松发布 MCVE。对不起。尽管如此,我已经指出,工作线程的每个调用都会启动一个进程(一个 unix 进程),并且该进程读取文件并写入另一个文件(到同一目录中),如果考虑到多线程环境,File.exists() 会给出不好的结果其中目录的内容正在发生变化,nio.Files.exists() 可能会给出更可靠的结果?
-
"寻求调试帮助的问题(“为什么这段代码不起作用?”)必须包括所需的行为、特定问题或错误以及在问题本身中重现它所需的最短代码。没有明确的问题陈述对其他读者没有用处。”
-
@SteveCohen 不。您的代码中很可能存在错误。但是很难说是什么,我们不知道你的哪些工作做了什么。以哪个顺序和哪个线程,或者是否有任何竞争条件等。
-
另外,我怀疑您的程序正在产生新的 unix 进程。它可能只是产生本地线程,这与另一个包含 JVM 的整个进程完全不同。如果你是
ps --forest aux | grep java,不会有 30 个 jvm 进程,只有一个。
标签: java multithreading nio java-io jdk1.7