1. 程式人生 > 實用技巧 >Java Unsafe CAS 小試

Java Unsafe CAS 小試

Unsafe 在 Java 標準庫和框架中被大量使用,本文主要介紹如何使用 sun.misc.Unsafe 包實現 CAS 操作。

Example

public class UnsafeExample {

    private volatile int foo;

    private static final Unsafe U;
    private static final long FOO;

    static {
        try {
            // 類載入器限制 無法直接使用
            // U = Unsafe.getUnsafe();

            // 通過反射
            U = getUnsafeByReflection();
            Class<?> clazz = UnsafeExample.class;
            FOO = U.objectFieldOffset(clazz.getDeclaredField("foo"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }

    private static Unsafe getUnsafeByReflection() {
        try {
            Class<?> unsafeClazz = Class.forName("sun.misc.Unsafe");
            Field f = unsafeClazz.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            return (Unsafe) f.get(null);
        } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    private void casUpdate(int x) {
        while (true) {
            final int snapsnot = this.foo;
            if (U.compareAndSwapInt(this, FOO, snapsnot, x)) break;
        }
    }

    public static void main(String[] args) {
        UnsafeExample example = new UnsafeExample();
        new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                example.casUpdate(i);
                System.out.println(example.foo);
            }
        }).start();
        new Thread(() -> {
            for (int i = 100; i < 200; i++) {
                example.casUpdate(i);
                System.out.println(example.foo);
            }
        }).start();
    }

}

小結

Unsafe 在 ConcurrentHashMap 使用較多,上方 example 主要借鑑了 ConcurrentHashMap 操作 Unsafe 的方式。

需要注意的是 Unsafe.getUnsafe() 會檢查當前類載入器是否合法,在設定上 Unsafe 僅適用於標準庫等場景,本文通過反射的方式獲取 Unsafe 物件。

直接使用Unsafe.compareAndSwapInt等方法細節上不同於 AtomicInteger ,需要自行新增自旋邏輯。本質上 AtomicInteger 等類也是藉助 Unsafe 類方法完成 CAS 操作的。下方為 AtomicInteger 相關原始碼:

    public final int getAndAddInt(Object o, long offset, int delta) {
        int v;
        do {
            v = getIntVolatile(o, offset);
        } while (!compareAndSwapInt(o, offset, v, v + delta));
        return v;
    }