質問

I can have 2 Java processes share a region of memory via MappedByteBuffer.

But if these 2 processes can simultaneously read from or write to a byte at offset x (from start of the shared memory), how do I synchronize access to this byte?

Here's the desired usage:

Assumption: There's only 1 thread per process that will attempt the read-write access.

                           |
========================== | ============================
     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.)
========================== | ============================
                           |

Time flows downward in the above diagram.

So, only process A or process B (but never both) can be executing Lines 1 through 20 at a given time.

役に立ちましたか?

解決

There is no standard way of doing this in Java. What you can try doing is use Unsafe, to create your own lock as I do in this library AbstractByte in OpenHFT/Java-Lang see the tryLock, busyLock, unlock methods.

I haven't test how well this works across processes. At present I only have a single process writing.

If you have one writer and one reader, you don't need to lock the record. You can use a memory barrier or a timestamp guard.

A simple timestamp guard can work like this.

Writer:

- 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

Reader:

- 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.

Of course if you have a single value, you just need an ordered write and a volatile read so not locking at all.


EDIT: I have added an example LockingViaMMapMain and LockingViaFileLockMain This uses two processes to lock records in a shared memory mapped files. One process locks and sets the flag to false (only if true) and the second set the flag to true (only if false)

The two processes print on my laptop

Toggled 10,000,128 times with an average delay of 31 ns

For comparison, the benchmark using FileLock looks like

Toggled 10,000,128 times with an average delay of 2,402 ns

FileLock is pretty quick, 2.4 micro-seconds, but on my machine using individual locks in the files was 80x faster.

If FileLock is fast enough you should use it because

  • it doesn't alter the file.
  • the lock is automatically released if the process dies.
  • it should work for network mounted files systems. I wouldn't expect the internal lock to work between systems.

The interesting code reads

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);
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top