我有一个在 java JDK 1.7 下运行的批处理进程。它在具有 RHEL、2.6.18-308.el5 #1 SMP 的系统上运行。
此过程从数据库获取元数据对象的列表。它从该元数据中提取文件的路径。该文件可能实际存在,也可能不存在。
该过程使用ExecutorService(Executors.newFixedThreadPool()
) 启动多个线程。每个线程都运行一个 Callable,该进程启动一个进程,该进程读取该文件并在该输入文件存在时写入另一个文件(并记录结果),如果该文件不存在则不执行任何操作(记录该结果除外)。
我发现这种行为是不确定的。尽管每个文件的实际存在始终是恒定的,但运行此过程不会给出一致的结果。它通常会给出正确的结果,但有时会发现一些确实存在的文件并不存在。如果我再次运行相同的进程,它将找到之前所说的不存在的文件。
为什么会发生这种情况,是否有其他更可靠的方法?当其他线程尝试读取目录时,在多线程进程中写入文件是否是错误?较小的线程池会有帮助吗(目前为 30)?
UPDATE:以下是此场景中工作线程调用的 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,换句话说,不是多线程执行此操作。