我之所以提出这个问题,是因为我想知道内存映射文件何时发生了更改。所以我可以适当地处理第二个程序。
手表服务有可能做到这一点吗?
编辑:
为了获取事件,我使用了来自java文档的演示代码。
我还在PowerShell中使用了Get-Content -Path "test.txt" -Wait
来监视文件的更改。
这是我为测试编写的代码。在TTT类中使用不同的值多次运行它不会触发事件,但在打开文件时会发生更改。
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class App1 {
// private static long count = 1_000_000L; // ~1MB
private static long size = 100_000L; // ~100KB
public static void main(String[] args) throws IOException, InterruptedException {
RandomAccessFile memoryMappedFile = new RandomAccessFile("test/test.txt", "rw");
MappedByteBuffer out = memoryMappedFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, size);
// Changing data in here and running again wont trigger an event
// But the changes are actually there if you open the file
TTT t = new TTT("Somebody once told me", System.currentTimeMillis());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] allBytes;
try {
ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(t);
os.flush();
allBytes = bos.toByteArray();
} finally { try { bos.close(); } catch (IOException ex) {} }
out.put(allBytes);
memoryMappedFile.close();
out.clear();
}
}
class TTT implements Serializable {
public String s;
public long a;
public TTT(String s, long a) {
this.s = s;
this.a = a;
}
}
内存映射文件是否被Watch Service API识别?
我认为答案是否定的。
在Linux中,Java Watch Service API使用inotify(7)
实现,内存映射文件使用mmap(2)
实现。
inotify的手动输入在限制部分这样说:
"inotify API不报告由于mmap(2(、msync(2(和munmap(2(而可能发生的文件访问和修改">
这取决于您创建的打开和修改文件的文件映射。
与传统的文件句柄一样,文件映射可以是可写的,也可以是只读的。
前两种映射模式MapMode.READ_ONLY
和MapMode.READ_WRITE
是相当明显的。它们指示您是希望映射为只读还是允许修改映射文件。
第三种模式MapMode.PRIVATE
表示您想要一个写时拷贝映射。这意味着,通过put((进行的任何修改都将导致只有MappedByteBuffer
实例才能看到的数据的私有副本。
不会对基础文件进行任何更改,当缓冲区被垃圾回收时,所做的任何更改都将丢失。即使写时复制映射阻止了对基础文件的任何更改,您也必须打开该文件进行读/写,才能设置MapMode.PRIVATE
映射。这对于返回的MappedByteBuffer
对象允许put()s
是必要的。
此外,我们还查看事件,以切换WatchService
可以做出反应。
StandardWatchEventKinds.ENTRY_CREATE
–在监视目录中创建新条目时触发。这可能是由于创建了新文件或重命名了现有文件StandardWatchEventKinds.ENTRY_MODIFY
–当被监视目录中的现有条目被修改时触发。所有文件编辑都会触发此事件。在某些平台上,即使更改文件属性也会触发它StandardWatchEventKinds.ENTRY_DELETE
–在监视目录中删除、移动或重命名条目时触发StandardWatchEventKinds.OVERFLOW
–触发以指示丢失或丢弃的事件。我们不会太关注它
考虑到这一点,可以说。。。
- 如果映射模式为
READ_ONLY
,则不会触发上述事件,因此不会进行检测 - 如果映射模式为
PRIVATE
,则不会触发上述事件,因为您正在使用文件的私有副本 - 现在最有趣的案例是
READ_WRITE
。由于此操作会在某个时刻修改磁盘驱动器上的文件,因此在某个时候会通知操作系统,然后它将通知WatchService
API
WatchService javadoc 中也应注意以下警告
"观察来自文件系统的事件的实现旨在直接映射到本地文件事件通知功能(如果可用(,或者在本地功能不可用时使用基元机制(如轮询(。因此,关于如何检测事件、事件的及时性以及是否保留其顺序的许多细节都是高度特定于实现的。例如,当被监视目录中的文件被修改时,在某些实现中可能会导致单个ENTRY_MODIFY事件,但在其他实现中会导致多个事件。短期文件(指创建后很快删除的文件(可能不会被周期性轮询文件系统以检测更改的原始实现检测到。如果监视的文件不在本地存储设备上,则如果可以检测到对该文件的更改,则这是特定于实现的。特别是,不需要检测在远程系统上执行的文件更改">