1

我正在编写自己的AtomicLong类,但我发现我拥有的功能比 Unsafe 类中提供的功能慢得多。我想知道为什么?

以下是我拥有的代码:

public interface Counter {
    void increment();
    long get();
}


public class PrimitiveUnsafeSupportCounter implements Counter{

    private volatile long count = 0;
    private Unsafe unsafe;
    private long offset;

    public PrimitiveUnsafeSupportCounter() throws IllegalAccessException, NoSuchFieldException {
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        this.unsafe = (Unsafe) f.get(null);
        this.offset = this.unsafe.objectFieldOffset(PrimitiveUnsafeSupportCounter.class.getDeclaredField("count"));
    }

    @Override
    public void increment() {

        this.unsafe.getAndAddLong(this, this.offset, 1);
    }

    @Override
    public long get() {
        return this.count;
    }
}

public class CounterThread implements Runnable {

    private Counter counter;

    public CounterThread(Counter counter){
        this.counter = counter;
    }
    @Override
    public void run() {

        for (int i = 0; i < 100000; i ++){
            this.counter.increment();
        }
    }
}

class Test{

    public static void test(Counter counter) throws NoSuchFieldException, IllegalAccessException, InterruptedException {

        ExecutorService executor = Executors.newFixedThreadPool(1000);

        long start = System.currentTimeMillis();
        for (int i = 0 ; i < 1000; i++){
            executor.submit(new CounterThread(counter));
        }

        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.MINUTES);
        long stop = System.currentTimeMillis();

        System.out.println(counter.get());
        System.out.println(stop - start);
    }

}

public class Main {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InterruptedException {

        Counter primitiveUnsafeSupportCounter = new PrimitiveUnsafeSupportCounter();
        Test.test(primitiveUnsafeSupportCounter);

    }

}

完成上述代码大约需要 3000 毫秒。但是,如果我使用下面的代码而不是this.unsafe.getAndAddLong(this, this.offset, 1);.

long before;
do {
     before = this.unsafe.getLongVolatile(this, this.offset);
} while (!this.unsafe.compareAndSwapLong(this, this.offset, before, before + 1));

我浏览了源代码,getAndAddLong发现它和上面的代码几乎一样,所以我应该错过什么?

4

1 回答 1

0

那是 JVM 固有的和手写的循环版本具有非常低效的编译代码。在 x86 上,您可以通过lock前缀获得此类读取-修改-写入操作的原子版本。请参阅英特尔手册8.1.2.2 软件控制的总线锁定

为了显式地强制 LOCK 语义,软件可以在使用 LOCK 前缀和以下指令来修改内存位置时使用它们。

特别是你可以有类似的东西lock add op1 op2。在您的示例中,您测试了结果cmpxchg并进行了一些明显较慢的跳转。此外,据我所知,x86 易失性访问需要某种mfencelock来确保内存排序。

于 2019-04-20T20:03:53.377 回答