ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

JDK8---UnSafe类实现CAS乐观锁

2020-12-04 13:01:08  阅读:162  来源: 互联网

标签:CAS Unsafe UnSafe class --- int static public size


CAS:CompareAndSwap(比较并交换),简单点说,内存地址V,旧值为A,当要修改为新值B的时候,先判断V当前的值是不是A,如果是,则将V的值修改为B,否则失败。

那么JDK8中是怎么实现的呢?(ConCurrentHashMap、Atomic、AQS等常见并发类的底层实现都有它)

在sun.misc包下有一个 UnSafe类(在rt.jar包下面):

public final class Unsafe {
private Unsafe() {//静态构造方法
    }

    public static Unsafe getUnsafe() {
        Class var0 = Reflection.getCallerClass();
        if (!VM.isSystemDomainLoader(var0.getClassLoader())) {//主要判断类的加载器
            throw new SecurityException("Unsafe");
        } else {
            return theUnsafe;
        }
    }
}

构造方法私有,压根不让用。唯一暴漏的getUnsafe方法也不能使用。通过Reflection.getCallerClass() 拿到当前调用方法的实际类,从下面代码可以看到只要实际类的类加载器不为null,直接抛异常。因为rt.jar是由 bootstrap类加载器 来加载的,在Java中看不到的(null),而我们的代码基本是由 AppClassLoader 加载的。

public static boolean isSystemDomainLoader(ClassLoader loader) {
        return loader == null;
    }

那怎么办呢?Java的重量级选手-反射 上场了。通过反射,很简单就拿到类Unsaf的实例。

Field f = Unsafe.class.getDeclaredFields()[0];
f.setAccessible(true);
Unsafe u = (Unsafe) f.get(Unsafe.class);

我们拿AtomicInteger来分析它的用法:

private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;

//以原子方式将当前值增加一
public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

AtomicInteger类属于rt.jar包,所以可以直接getUnsafe()。再来看unsafe.getAndAddInt(),总共有三个参数,第一个是当前对象,第二个是valueOffset(下面分析),第三个 加1。

valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value")) 这个很重要,Unsafe的CAS操作都是建立在它之上的

从方法名称上来看 对象字段的偏移量(后面会详细举例说明),因为一个Java对象的大小在类加载完成后是可完全确定的。

public final int getAndAddInt(Object obj, long offSet, int a) {
        int b;
        do {
            b= this.getIntVolatile(obj, offSet);
        } while(!this.compareAndSwapInt(obj, offSet, b, b+ a));

        return var5;
    }

public native int getIntVolatile(Object obj, long offSet);

public final native boolean compareAndSwapInt(Object obj, long offSet, int A, int B);

上面代码的参数名称做了部分修改,看起来更加清楚。

getIntVolatile方法->获取obj对象内存地址上偏移量offSet地址上属性的int值

compareAndSwapInt方法->设置obj对象内存地址上偏移量offSet地址上属性的值,如果当前属性值为A,则改为B返回true,否则返回false。

下面来看测试代码,根据结果一步步验证我们的想法:

public class CasTest {
    private static final long SIZE;
    private static Unsafe u;

    static {
        try {
            Field f = Unsafe.class.getDeclaredFields()[0];
            f.setAccessible(true);
            u = (Unsafe) f.get(Unsafe.class);
            SIZE = u.objectFieldOffset
                    (CasTest.class.getDeclaredField("size")); //获取字段在CasTest对象上的偏移量
        } catch (Exception e) {
            throw new Error(e);
        }
    }

    private int size = 2;

    public void t() {
        System.out.println(size);
        if (u.compareAndSwapInt(this, SIZE, 2, 1))// 比较的是this内存上 偏移量为 SIZE,在这里实际就是指size。期望值是2,想改成1
            System.out.println(true);
        System.out.println(size);
        size = 3;
        if (u.compareAndSwapInt(this, SIZE, 3, 7)) // 实际修改size = 7,不是改的SIZE
            System.out.println(true);
        System.out.println(SIZE);
        System.out.println(size);
    }
}

最终结果为:

2
true
1
true
12
7

Unsafe虽然从名字看不安全,Java这么多线程安全的工具类的都是在它之上实现的,可见它是安全的,至于它的其他用法,就由各位去自行挖掘啦。

本篇到此结束,拜拜!

标签:CAS,Unsafe,UnSafe,class,---,int,static,public,size
来源: https://blog.csdn.net/weixin_53134321/article/details/110632979

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有