我创建了一个Java进程,它写入纯文本文件和另一个使用该文本文件的Java进程。然后'consumer'读取删除文本文件。为简单起见,我不使用文件锁(我知道它可能会导致并发问题)。
'消费者'流程每隔30分钟从crontab
开始运行一次。 “生产者”流程目前只是将从标准输入接收的任何内容重定向到文本文件。这仅用于测试 - 将来,'生产者'流程将自行编写文本文件。
'producer'进程打开FileOutputStream
一次,并继续写入文本文件,使用此输出流。问题是当'消费者'删除文件时。因为我在UNIX环境中,所以这种情况“优雅地”处理:'生产者'继续工作,好像什么也没发生,因为文件的inode
仍然有效,但是找不到文件在文件系统中。 This thread提供了一种使用C来处理这种情况的方法。因为我使用的是Java,它是可移植的,因此隐藏了所有特定于平台的功能,所以我无法使用那里提供的解决方案。
在FileOutputStream
仍处于打开状态时,Java中是否有可移植的方法来检测文件被删除?
答案 0 :(得分:2)
这对于您的流程进行沟通并不是一种强有力的方式,我建议的最好方法是停止这样做。
据我所知,C程序没有可靠的方法来检测正在写入的文件何时被取消链接,更不用说Java程序了。 (accepted answer you've linked to只能轮询目录条目以查看它是否仍然存在;我认为这不够强大)。
正如您所注意到的,UNIX并不认为打开文件取消链接是不正常的(实际上,创建命名的临时文件,获取文件句柄,然后从目录中删除它以便其他进程是一种惯例在阅读和写作之前无法得到它。)
如果您必须使用文件,请考虑让您的消费者轮询目录。对于正在编写的文件有.../pending/
目录,对于准备好处理的文件有.../inbox/
。
pending/
。inbox/
- 只要两个目录都在同一个文件系统上,这只是一个重新链接,所以{{1}中的文件永远不会完整}。inbox/
中查找文件,读取它们并在完成后删除。如果最终有多个消费者,您可以使用更多目录来增强此功能,但是没有立即需要。
但是轮询文件/目录总是有点脆弱。考虑数据库或消息队列。
答案 1 :(得分:0)
您可以检查文件名本身是否存在:
if (!Files.exists(Paths.get("/path/to/file"))) {
// The consumer has deleted the file.
}
但无论如何,消费者不应等待让生产者在读取&之前完成文件的写入。删除它?如果确实如此,你就不会遇到这个问题。
答案 2 :(得分:0)
要按照您打算这样做的方式解决这个问题,您可能需要查看JNI,它允许您从Java中调用c / c ++函数,但这可能还需要您为包装库编程stat / fstat首先(用c / c ++)。
但是 - 这会让你头疼。
这可能是一种解决方法,现在不需要对代码进行太多更改(我假设)。 每次生成新数据时,您都可以让生产者写入新文件。根据数量,您可能希望对数据进行分组,以便目录不会充满文件。例如,每分钟一个文件包含到目前为止生成的所有数据。 另外,最好先将文件写入另一个目录,然后将它们移动到您的消费者输入目录 - 我在这里有点偏执,因为可能会有一些竞争条件导致您出现一些数据损失...... - 在已经写完所有内容之后移动文件然后移动它们将确保没有数据丢失。
希望这有助于祝你好运:)