我可以让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.


当然,如果只有一个值,则只需要有序的写操作和易失的读操作,因此根本不会锁定。



编辑:我添加了一个示例LockingViaMMapMainLockingViaFileLockMain,它使用两个过程将记录锁定在共享内存映射文件中。一个进程将标志锁定并设置为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);
}

10-05 17:49