java中CAS实现原理

java中CAS实现原理,第1张

java中CAS实现原理

java中在Unsafe中提供了三种CAS *** 作

compareAndSwapInt(),compareAndSwapObject(),compareAndSwapLong()

//参数含义:对象,属性的内存偏移量,属性期望值,属性更新值
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);

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

public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

偏移量:内存中的对象包含对象头和对象实例数据,而对象头占8个字节,对于64位 *** 作系统的压缩指针占4个字节, 所以我们一般说对象头占12个字节;例如对于一个test对象,x偏移量为这个对象的对象头即12个字节,y的偏移量为16

cas *** 作修改Test类的x变量。

public class CASTest {

    public static void main(String[] args) {
        Test test = new Test();
        Unsafe unsafe = UnsafeFactory.getUnsafe();
        long xOffset = UnsafeFactory.getFieldOffset(unsafe, Test.class, "x");
        System.out.println(xOffset); //12
        long yOffset = UnsafeFactory.getFieldOffset(unsafe, Test.class, "y");
        System.out.println(yOffset); //16
        unsafe.compareAndSwapInt(test, xOffset, 0, 1);
        System.out.println(test.x);
    }
    
    static class Test {
        int x;
        int y;
    }
}

CAS可以保证原子性,但是不能保证有序性和可见性,所以一般地,CAS配合volatile使用可以保证线程安全。底层最终通过执行一个cas指令(原子 *** 作修改变量值),通过期望值和内存中的实际值进行比较,比较的结果如果相等则返回的是旧值(期望值)表示CAS *** 作成功,不相等则返回内存中实际值表示CAS *** 作失败。

CAS实现线程安全的 *** 作

public class CASTest {

    private static int sum = 0;
    private static CASLock casLock = new CASLock();

    public static void main(String[] args) throws InterruptedException {
        for (int i=0; i<10; i++) {
            new Thread(() -> {
                for (;;) {
                    if (casLock.getState() == 0 && casLock.cas()) {
                        try {
                            for (int j = 0; j < 10000; j++) {
                                sum++;
                            }
                        } finally {
                            casLock.setState(0);
                        }
                        break;
                    }
                }
            }).start();
        }
        Thread.sleep(2000);
        System.out.println(sum);
    }
}
public class CASLock {

    private volatile int state = 0;
    private static final Unsafe UNSAFE;
    private static final long OFFSET;
    static {
        UNSAFE = UnsafeFactory.getUnsafe();
        OFFSET = UnsafeFactory.getFieldOffset(UNSAFE, CASLock.class, "state");
    }
    
    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    public boolean cas() {
        return UNSAFE.compareAndSwapInt(this, OFFSET, 0, 1);
    }
}

jdk中juc包下原子类都是通过cas实现线程安全的。

ABA的问题可以通过版本号来解决

LongAdder、DoubleAdder原理

在高并发下,CAS *** 作会有大量的线程自旋,导致浪费线程资源,而为了提高执行效率,将V值拆分为多个变量,多个线程同时对各自的变量进行cas *** 作,所有线程执行完成后,将所有变量进行累加统计。其思想与jdk8中ConcurrentHashMap统计元素个数类似,LongAdder、DoubleAdder也实现了该思想。LongAdder中定义了base变量和cell数组变量,通过hash对Cell数组初始化和累加 *** 作,最后将base和Cell数组所有的数进行累加得到结果。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5683661.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存