1.2.2 线程安全之原子操作

竞态条件与临界区

共享资源

不可变对象

原子操作定义

  • 原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分(不可中断性)。
  • 将整个操作视作一个整体,资源在该次操作中保持一致,这是原子性的核心特征。

CAS机制

	volatile int value = 0;

    static Unsafe unsafe; // 直接操作内存,修改对象,数组内存....强大的API
    private static long valueOffset;

    static {
        try {
            // 反射技术获取unsafe值
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);

            // 获取到 value 属性偏移量(用于定于value属性在内存中的具体地址)
            valueOffset = unsafe.objectFieldOffset(LockDemo1.class
                    .getDeclaredField("value"));

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public void add() {
        // TODO xx00
        // i++;// JAVA 层面三个步骤
        // CAS + 循环 重试
        int current;
        do {
            // 操作耗时的话, 那么 线程就会占用大量的CPU执行时间
            current = unsafe.getIntVolatile(this, valueOffset);
        } while (!unsafe.compareAndSwapInt(this, valueOffset, current, current + 1));
        // (可能会失败)CAS命令,如果current值发生变化,则操作失败
    }
复制代码
  • 有兴趣的童鞋可以了解下Unsafe的源码,类似于直接操作内存的方法。JVM调用系统本地方法操作数据,保证只有一个线程可以访问到本地方法操作,实现原子操作。

J.U.C包内的原子操作封装类

  • java.until.concurrent线程工具类(J.U.C)

CAS的三个问题

  • JAVA提供了ABA的解决办法:原子更新带有版本号的引用类型。CAS在比对是不仅比对值,还会比对版本号。
  • Atomic原子操作封装类,将i++分成了几步(读取值,更新值,赋值),使用了底层的CAS机制,并且不仅仅存了值,还存了时间戳版本号。

猜你喜欢

转载自juejin.im/post/5d838468518825781671e2ac