Aaron
Aaron

Reputation: 43

Could someone explain the code as follows?

import sun.misc.Unsafe;
import sun.nio.ch.DirectBuffer;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class PingPongMapMain {
    public static void main(String... args) throws IOException {
        boolean odd;
        switch (args.length < 1 ? "usage" : args[0].toLowerCase()) {
            case "odd":
                odd = true;
                break;
            case "even":
                odd = false;
                break;
            default:
                System.err.println("Usage: java PingPongMain [odd|even]");
                return;        
        }
        int runs = 10000000;
        long start = 0;
        System.out.println("Waiting for the other odd/even");
        File counters = new File(System.getProperty("java.io.tmpdir"),"counters.deleteme");        
        counters.deleteOnExit();
        try (FileChannel fc = new RandomAccessFile(counters, "rw").getChannel()) {
        MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
        long address = ((DirectBuffer) mbb).address();
        for (int i = -1; i < runs; i++) {
            for (; ; ) {
                long value = UNSAFE.getLongVolatile(null, address);
                boolean isOdd = (value & 1) != 0;
                if (isOdd != odd)
                    // wait for the other side.
                    continue;
                    // make the change atomic, just in case there is more than one odd/even process
                if (UNSAFE.compareAndSwapLong(null, address, value, value + 1))
                    break;
            }
            if (i == 0) {
                System.out.println("Started");
                start = System.nanoTime();
                }
            }
        }
        System.out.printf("... Finished, average ping/pong took %,d ns%n",
            (System.nanoTime() - start) / runs);
    }

   static final Unsafe UNSAFE;
   static {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            UNSAFE = (Unsafe) theUnsafe.get(null);
        } catch (Exception e) {
        throw new AssertionError(e);
        }
    }
}

This example shows "Thread safe access to direct memory", and it used for share data between processes.When run this in two programs, one with odd and the other with even. you can see that each process is changing data via persisted shared memory.

But I am confused about a few things:

  1. what does the tmp file used for;
  2. what does UnSafe used for here;
  3. why unsafe use here;
  4. why cast a field to a UnSafe type but not sun.misc.Unsafe.getUnsafe()?

Upvotes: 1

Views: 260

Answers (1)

Stephen C
Stephen C

Reputation: 719661

The program illustrates how two separate Java processes can communicate via a shared memory segment implemented via a memory mapped file. One process makes a number at a location in the shared segment odd, and the other makes it even ... repeatedly ... "ping pong-ing" between the two processes.

what does the tmp file used for

It effectively provides a way for two processes to share a memory segment. A file is created, and mapped into the address spaces of two processes. The net result is that the two processes are sharing the same area of memory.

what does UnSafe used for here;

    long value = UNSAFE.getLongVolatile(null, address);

Reads 64 bits from address with a read barrier; i.e. ensuring that it is reading from main memory (not cache memory)

    UNSAFE.compareAndSwapLong(null, address, value, value + 1));

Performs an atomic compare and swap. If the value at address is value, then atomically change it to value + 1.

why unsafe use here;

Because it is a good way1 perform those low-level operations (with precisely those semantics. (Things like Java primitive mutexes and Lock don't have specified "Java memory model" semantics when the threads using them are in separate processes.)

why cast a field to a UnSafe type but not sun.misc.Unsafe.getUnsafe()?

This is some nasty stuff designed to get around JVM restrictions. If you just call Unsafe.getUnsafe() you will typically get a SecurityException.

Reference:

Just look the other way .....


1 - Possibly the only way apart from writing non-portable native code. But bear in mind that the Unsafe class is designed for JVM internal use, and the API and behavior could change without any notice.

Upvotes: 2

Related Questions