CAS原理

1 篇文章 0 订阅

CompareAndSwap

乍看中文含义是:比较和交换。实现的逻辑就是先比较目标值,如果是期望值则更新,否则返回false。那么这个操作怎么保证原子性呢?具体交给各操作系统底层去实现,jvm会根据不同的操作系统编写不同的c++代码实现,java的api中凡事标注native的方法都是调用底层c++方法

java中CAS的调用

java提供了一个访问入口类:sun.misc.Unsafe

如果我想模仿大师Doug Lea一样写些底层操作的代码。可以直接使用Unsafe 类吗?答案是可以,但官方不建议。Unsafe类是不能直接创建的,也不提供静态工厂方法(其实提供了getUnsafe(),但只给自己类加载器家装的类使用)。那怎么办? Oracle没有把路堵死,利用反射方法就可以拿到theUnsafe成员属性,也就是一个Unsafe实例,这样我们就能直接调用它里面的很多有用的方法。直接上代码:

public class Test {
    public static void main(String[] args) throws NoSuchFieldException,
            SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");//反射得到theUnsafe对应的Field对象
        field.setAccessible(true);//获取Field访问权限
        Unsafe unsafe = (Unsafe) field.get(null);//传入null是因为该Field为static的
        System.out.println(unsafe);
    }
}

Unsafe常用方法

  1. allocateInstance

    作用:直接分配对象空间,但不初始化(不会调用构造方法)。
import sun.misc.Unsafe;
import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws NoSuchFieldException,
            SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);
        System.out.println(unsafe);

        //通过Unsafe类实例化的对象,不会调用默认构造方法
        Student student1 = (Student) unsafe.allocateInstance(Student.class);
        System.out.println(student1);

        //正常调用
        Student Student2 = new Student();
        System.out.println(Student2);
    }
}

class Student {
    private String name = "";
    private int age = 0;
    public Student() {
        this.name = "zangsan";
        this.age = 18;
    }
    @Override
    public String toString() {
        return name + ": " + age;
    }
}

输出结果:

sun.misc.Unsafe@776ec8df
null: 0
zangsan: 18

我们看到通过allocateInstance操作生成的对象,输出:null: 0

  1. objectFieldOffset (非常有用 很重要)

    作用:获取属性相对该对象内存起始地址的偏移量。CAS操作就是根据属性的偏移量直接操作内存。
long nameOffeSet=unsafe.objectFieldOffset(Student.class.getDeclaredField("name"));
 System.out.println(nameOffeSet);

输出:

16
  1. compareAndSwapInt、compareAndSwapObject、compareAndSwapLong
    以compareAndSwap开头的方法还有很多,JUC里面大量调用类此类方法判断线程能否原子得更新某个属性。

    compareAndSwapInt(Object var1, long var2, int var4, int var5)
    var1:要操作的对象实例
    var2:属性偏移量,就是根据objectFieldOffset算出来的long值
    var4:期望值
    var5:更新的目标值

Student Student2 = new Student();
System.out.println(Student2);

//将Student2 的age由18 改为28
long ageOffeSet=unsafe.objectFieldOffset(Student.class.getDeclaredField("age"));
unsafe.compareAndSwapInt(Student2,ageOffeSet,18,28);
System.out.println(Student2);
zangsan: 18
zangsan: 28

可以看到我们拿到age属性的偏移量后,就能直接将原值18更新为28.

  1. getObjectVolatile
    类似的getIntVolatile、getBooleanVolatile、getByteVolatile 等等都是在多线程环境下,保证能拿到实时更新的数据。jmm的volatile语义实现。具体就是通过插入内存屏障指令,禁止处理器冲排序和处理器的缓存锁实现来保证。
System.out.println(unsafe.getIntVolatile(Student2,ageOffeSet));
28

ConcurrentHashMap 中使用CAS

  • 原子获取和设置Node节点:
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                        Node<K,V> c, Node<K,V> v) {
        return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
    }

    static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
        U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
    }

  • Map初始化时,原子更新Node容量属性:
U.compareAndSwapInt(this, SIZECTL, sc, -1)

sizeCtl =-1 表示当前线程竞争成功,只能当前线程能执行初始化操作,其他线程只能自旋等待( Thread.yield(); // lost initialization race; just spin)

  • Map在新增数据(put操作)后原子更新数据总量
    注意:Map的总量是BASECOUNT加上CounterCell[] 数组中存放的数据量的总和。
private final void addCount(long x, int check) {
        CounterCell[] as; long b, s;
        if ((as = counterCells) != null ||
            !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
            CounterCell a; long v; int m;
            boolean uncontended = true;
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
                !(uncontended =
                  U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
                fullAddCount(x, uncontended);
                return;
            }
            if (check <= 1)
                return;
            s = sumCount();
        }
        if (check >= 0) {
            Node<K,V>[] tab, nt; int n, sc;
            while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
                   (n = tab.length) < MAXIMUM_CAPACITY) {
                int rs = resizeStamp(n);
                if (sc < 0) {
                    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                        sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
                        transferIndex <= 0)
                        break;
                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
                        transfer(tab, nt);
                }
                else if (U.compareAndSwapInt(this, SIZECTL, sc,
                                             (rs << RESIZE_STAMP_SHIFT) + 2))
                    transfer(tab, null);
                s = sumCount();
            }
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值