我最近看到了这篇文章,很好地介绍了内存映射文件及其如何在两个进程之间共享。下面是读取文件的进程代码:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class MemoryMapReader {
/**
* @param args
* @throws IOException
* @throws FileNotFoundException
* @throws InterruptedException
*/
public static void main(String[] args) throws FileNotFoundException, IOException, InterruptedException {
FileChannel fc = new RandomAccessFile(new File("c:/tmp/mapped.txt"), "rw").getChannel();
long bufferSize=8*1000;
MappedByteBuffer mem = fc.map(FileChannel.MapMode.READ_ONLY, 0, bufferSize);
long oldSize=fc.size();
long currentPos = 0;
long xx=currentPos;
long startTime = System.currentTimeMillis();
long lastValue=-1;
for(;;)
{
while(mem.hasRemaining())
{
lastValue=mem.getLong();
currentPos +=8;
}
if(currentPos < oldSize)
{
xx = xx + mem.position();
mem = fc.map(FileChannel.MapMode.READ_ONLY,xx, bufferSize);
continue;
}
else
{
long end = System.currentTimeMillis();
long tot = end-startTime;
System.out.println(String.format("Last Value Read %s , Time(ms) %s ",lastValue, tot));
System.out.println("Waiting for message");
while(true)
{
long newSize=fc.size();
if(newSize>oldSize)
{
oldSize = newSize;
xx = xx + mem.position();
mem = fc.map(FileChannel.MapMode.READ_ONLY,xx , oldSize-xx);
System.out.println("Got some data");
break;
}
}
}
}
}
}
我有一些关于这种方法的评论/问题:
如果我们只在空文件上执行读取器,即运行
long bufferSize=8*1000;
MappedByteBuffer mem = fc.map(FileChannel.MapMode.READ_ONLY, 0, bufferSize);
long oldSize=fc.size();
这将分配8000字节,现在文件将被扩展。返回的缓冲区限制为8000,位置为0,因此,读取器可以继续读取空数据。发生这种情况后,读者将停止,因为
currentPos == oldSize
。假设现在写入程序进来了(代码被省略,因为大部分都很简单,可以参考网站)- 它使用相同的缓冲区大小,因此它将先写入8000个字节,然后再分配另外8000个字节,扩展文件。现在,如果我们假设该过程在此时暂停,然后回到读取器,那么读取器将看到文件的新大小并分配剩余部分(因此从位置8000到1600),然后再次开始读取,并读取出另一个垃圾数据……
我有一点困惑,是否有一种方法可以同步这两个操作。就我所看到的,调用
map
的任何操作可能会用一个真正的空缓冲区(填充为零)扩展文件,或者写入程序可能只是扩展了文件,但还没有写入任何内容……