深入CAS原理

JDK中所有的CAS到最后都要用到这个方法:

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);

这个方法有4个参数,奇怪的是序号不是1234,而是1245,没有javadoc和任何注释,是个native方法,代码实现是用C++写的。

这个方法到底干什么的?

下面来分析这个几个参数:

Object var1是要修改的目标对象。
long var2是要修改的对象的属性,为什么是整数,因为传入C++时,通过var1这个结构体指针和偏离值得到字段的。
这个偏离值怎么获得,通过下面这个方法,next是字段名:
UNSAFE.objectFieldOffset(k.getDeclaredField("next"));
Node对象有个属性next,cas方法compareAndSwapObject要修改的就是Node对象的next属性值,通过得到这个next的参数的偏离值比如12,来修改它。
Object var4是要对比的值,拿var4和Node对象的next属性对比,如果相同就用var5替换
Object var5要拿来替换的对象。

完整的代码如下:

import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.Objects;

/**
 * Created by TangHaiyang on 2019/8/19.
 * 验证cas的关键方法objectFieldOffset
 */
public class TestUnsafe {

    public static void main(String[] args) {
        Node node = new Node();
        node.seq=10;

        Node node2 = new Node();
        node2.seq=20;

        Node node3 = new Node();
        node3.seq=20;

        System.out.println(node2.equals(new Node()));
        System.out.println(node2.equals(node3));

        /**
         * 原子操作, 通过CAS方法更新node的next属性
         * 是否等于null,等于则用new node()替换并返回true,否则不做任何操作并返回false
         * 在jdk源码的下面几个集合中广泛用到了compareAndSwapObject,通常是跟null对比然后替换为指定的对象
         * ConcurrentHashMap
         * ConcurrentLinkedQueue
         * ConcurrentSkipListMap
         * ConcurrentLinkedDeque
         * SynchronousQueue
         */
        boolean flag1 = node.casNext(null,new Node());
        boolean flag2 = node.casNext(null,new Node());
        boolean flag3 = node.casNext(node2,new Node());
        System.out.println(flag1);
        System.out.println(flag2);
        System.out.println(flag3);
    }

    private static class Node{
        volatile Node next;
        volatile int seq;
        private static final sun.misc.Unsafe UNSAFE;
        private static final long nextOffset;
        static {
            try {
                /*
                sun.misc.Unsafe.getUnsafe()会得到一个SecurityException,这个类只有被JDK信任的类才能通过这个方法实例化
                但是可以通过反射拿到对应的实例
                UNSAFE = sun.misc.Unsafe.getUnsafe();
                */
                UNSAFE = getUnsafe();
                Class<?> k = Node.class;
                if(Objects.isNull(UNSAFE)) {
                    nextOffset = 0;
                }else {
                    nextOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("next"));
                }
            } catch (Exception e) {
                throw new Error(e);
            }
        }

        /**
         * 使用Unsafe CAS方法
         * @param cmp 目标值与cmp比较equal方法,如果相等就更新返回true;如果不相等就不更新返回false;
         * @param val 需要更新的值;
         * @return boolean
         */
        boolean casNext(Node cmp, Node val) {
            /*
             * compareAndSwapObject(Object var1, long var2, Object var3, Object var4)
             * var1 操作的对象
             * var2 操作的对象属性是个偏离值,对C++的结构体来说,var1是指针,指针加偏离值就能得到结构体成员,相当远java对象属性
             * var3 var2对应的属性与var3比较,相等才更新
             * var4 更新值
             */
            return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
        }

        /**
         * 获取Unsafe的方法
         * 获取了以后就可以愉快的使用CAS啦
         * @return Unsafe
         */
        private static Unsafe getUnsafe() {
            try {
                Field f = Unsafe.class.getDeclaredField("theUnsafe");
                f.setAccessible(true);
                return (Unsafe)f.get(null);
            } catch (Exception e) {
                return null;
            }
        }
    }
}
原文地址:https://www.cnblogs.com/geektcp/p/11377822.html