我有一个在 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,换句话说,不是多线程这个操作。