Java线程安全解决方案-volatile关键字 + CAS算法

599 阅读2分钟

JAVA并发处理维护操作一致性的方式有:

  1. 以加锁的方式维护操作原子性,一致性的问题。但是加锁的方式当发生锁碰撞会出现线程上下文切换和重新调度等性能损耗。
  2. 使用volatile关键字+CAS算法。volitile关键字保证数据的可见性,即线程私有的虚拟机栈中的变量更新会同步到内存中。CAS算法Compare And Swap保证数据更新的原子性。 CAS操作是计算机硬件级别的原子操作,即Compare and Swape
  3. 判断变量值是否和该线程读取时一致,
  4. 更新操作后的值。
  • 两个步骤从而保证操作一致性。但是会有ABA问题。

CAS算法的JAVA实现: CAS的实现可以用sun.misc下的Unsafe类,该类提供了硬件级别的原子性操作。Unsafe类中提供的方法都是native方法,由于可直接操作硬件,内存。该类"不安全"-Unsafe。 因此在加载该类类加载器,被限定为BootstrapClassLoader。因此无法在应用级别直接获取。以下为失败案例测试demo

public class UnsafeDemo {
    static final Unsafe UNSAFE = Unsafe.getUnsafe();
    static final long STATE_OFFSET;
    private volatile long state = 0;

    static {
        try {
            STATE_OFFSET = UNSAFE.objectFieldOffset(UnsafeDemo.class.getDeclaredField("state"));
        } catch (Exception e) {
            System.out.println(e.getLocalizedMessage());
            throw new Error(e);
        }
    }

    public static void main(String[] args) {
        UnsafeDemo unsafeDemo = new UnsafeDemo();
        boolean success = UNSAFE.compareAndSwapInt(unsafeDemo, STATE_OFFSET, 0, 1);
        System.out.println("success = " + success);
    }
}

执行结果为: D:\Java\jdk1.8\bin\java.exe ... Exception in thread "main" java.lang.ExceptionInInitializerError Caused by: java.lang.SecurityException: Unsafe at sun.misc.Unsafe.getUnsafe(Unsafe.java:90) at unsafe.UnsafeDemo.(UnsafeDemo.java:12)

可以使用反射直接获取Unsafe实例。

public class UnsafeRejectDemo {
    static final Unsafe UNSAFE;
    static final long STATE_OFFSET;
    private volatile long state = 0;

    static {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            UNSAFE = (Unsafe) field.get(null);
            STATE_OFFSET = UNSAFE.objectFieldOffset(UnsafeDemo.class.getDeclaredField("state"));
        } catch (Exception e) {
            System.out.println(e.getLocalizedMessage());
            throw new Error(e);
        }
    }

    public static void main(String[] args) {
        UnsafeRejectDemo unsafeRejectDemo = new UnsafeRejectDemo();

        // CAS操作,判断该实例中,定长偏移量的值是否为expect值,如果是,则与update值交换并返回true
        boolean success = UNSAFE.compareAndSwapInt(unsafeRejectDemo, STATE_OFFSET, 0, 1);
        System.out.println("state = " + unsafeRejectDemo.state);
        System.out.println("success = " + success);
    }
}

运行结果:

state = 1

success = true

Process finished with exit code 0