我可以让2个Java进程通过MappedByteBuffer
共享一个内存区域。
但是,如果这两个进程可以同时从偏移量x
读取或写入字节(从共享内存的起始位置开始),该如何同步对该字节的访问?
这是所需的用法:
假设:每个进程只有1个线程将尝试读写访问。
|
========================== | ============================
Process A | Process B
========================== | ============================
|
lock(sharedMemory[x]); | lock(sharedMemory[x]); (Line 1.)
... | ...
// read from byte at x | // write to byte at x
a = sharedMemory[x]; | sharedMemory[x] = b;
... | ...
unlock(sharedMemory[x]); | unlock(sharedMemory[x]); (Line 20.)
========================== | ============================
|
时间在上图中向下流动。
因此,在给定的时间,只有进程A或进程B(但绝不会两者都可以)执行1至20行。
最佳答案
在Java中,没有标准的方法可以做到这一点。您可以尝试使用Unsafe来创建自己的锁,就像在此库AbstractByte in OpenHFT/Java-Lang中一样,请参见tryLock,busyLock,unlock方法。
我还没有测试这在整个流程中的效果如何。目前我只有一个过程写作。
如果您只有一位作者和一位读者,则无需锁定记录。您可以使用内存屏障或时间戳保护。
一个简单的时间戳保护可以像这样工作。
作家:
- on start,
- if even, increment to odd with memory barriers.
- if odd, retry until even.
- perform multiple writes
- on finish, increment the counter to an even number
读者:
- on start, (with a read barrier)
- if even, take a copy of the counter.
- if odd, retry until even.
- read the multiple values.
- read the counter again, if the counter changed, loop until it doesn't.
当然,如果只有一个值,则只需要有序的写操作和易失的读操作,因此根本不会锁定。
编辑:我添加了一个示例LockingViaMMapMain和LockingViaFileLockMain,它使用两个过程将记录锁定在共享内存映射文件中。一个进程将标志锁定并设置为false(仅当为true时),第二个进程将标志设置为true(仅当为false时)
这两个过程在我的笔记本电脑上打印
Toggled 10,000,128 times with an average delay of 31 ns
为了进行比较,使用FileLock的基准看起来像
Toggled 10,000,128 times with an average delay of 2,402 ns
FileLock很快,只有2.4微秒,但是在我的机器上使用文件中的单个锁的速度快了80倍。
如果FileLock足够快,则应使用它,因为
它不会更改文件。
如果进程终止,该锁将自动释放。
它应该适用于网络安装的文件系统。我不希望内部锁在系统之间起作用。
有趣的代码读取
File tmpFile = new File(System.getProperty("java.io.tmpdir"), "lock-test.dat");
FileChannel fc = new RandomAccessFile(tmpFile, "rw").getChannel();
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, RECORDS * RECORD_SIZE);
ByteBufferBytes bytes = new ByteBufferBytes(mbb.order(ByteOrder.nativeOrder()));
// the lock and toggle.
bytes.busyLockLong(recordOffset + LOCK);
try {
boolean flag = bytes.readBoolean(recordOffset + FLAG);
if (flag == toggleTo) {
if (t % 100 == 0)
System.out.println("j: " + j + " is " + flag);
continue;
}
bytes.writeBoolean(recordOffset + FLAG, toggleTo);
break;
} finally {
bytes.unlockLong(recordOffset + LOCK);
}