cas

简介

synchronized轻量级锁使用cas(compare and swap)自旋抢锁,是cpu的原子操作,并且在用户态下面,开销较小.

Unsale类

主要提供了一些底层操作,比如:直接访问内存资源,自主管理内存资源.可以让我们直接通过指针来操作内存空间,因此是不安全的.他的操作是原子操作.

native方法,调用了底层的CPU指令,入参主要有:需要操作的对象,字段偏移,期望值,新值.

通过cas获取自增id

package com.lenovo.javautils.lock;

import com.lenovo.javautils.threads.MyThreadPoolExecutor;
import lombok.Getter;
import lombok.Setter;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

public class CasAddTest {
    @Getter
    @Setter
    private volatile int value;

    private static long offset = 0;
    private static Unsafe unsafe;
    @Getter
    private final AtomicInteger casCount = new AtomicInteger(0);

    static {
        try {
            unsafe = MyUnSafe.getUnsafe();
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
        Field monField = null;
        try {
            monField = CasAddTest.class.getDeclaredField("value");
            offset = unsafe.objectFieldOffset(monField);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        System.out.println("monField:" + monField);
    }

    public CasAddTest() throws NoSuchFieldException, IllegalAccessException {
    }

    public boolean cas(int old, int update) {
        return unsafe.compareAndSwapInt(this, offset, old, update);
    }

    public void add() {
        while (!cas(value, value + 1)) {
            System.out.println("cas");
            casCount.addAndGet(1);
        }
    }

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InterruptedException {

        CasAddTest casAddTest = new CasAddTest();
        casAddTest.setValue(100);
        System.out.println("offset:" + offset);
        CountDownLatch countDownLatch = new CountDownLatch(10);
        for (int i = 0; i < 10; i++) {
            MyThreadPoolExecutor.INSTANCE.getInstance().execute(
                    () -> {
                        casAddTest.add();
                        countDownLatch.countDown();
                    }
            );
        }
        countDownLatch.await();
        int value = casAddTest.getValue();
        System.out.println("result=" + value + "casCount=" + casAddTest.getCasCount());
    }

运行结果:

result=110casCount=1

根据结果分析,因为cpu竞争发生了一次自旋.

juc包中的atomic原子类也是基于以上,cas自旋+volatile关键字来实现的线程安全.

Cas的ABA问题

cas的aba问题主要发生在AB线程同时获取了值,B线程获取到值,将值设置为了B,然后又设置为了A.此时A线程是不知道的,依然可以正常设置值.

可以看到最后的结果,只能从桶里获取到e1.而e3处于流离态.

解决:

通过增加只能递增的版本号,A1B1A2,这样线程A在操作的时候,发现A2不是A1,就可以进行报错退出.

AtomicStampReference是jdk提供的带有版本号的原子性操作类.

优点:

通过自旋来减少cas重量级锁的使用.

劣势:

竞争太过激烈的情况下,会导致更多无效的自旋.

LongAdder

longAdder是一种以空间换时间的一种解决cas自旋的方案.用一种类似于hashmap的散列结构,key是抢资源线程,value为修改后的值.每个线程都在各自的cell操作,最终输出的时候,将base和各个cell的值累加.

AtomicReference

由于atomic基本数据类型,只能单个操作,当涉及到多个变量操作的时候,可以把变量放到引用类型原子类里面.

  public static void main(String[] args) {
        User user = new User(11,12);
        User user1 = new User(22,23);
        AtomicReference<User> userAtomicReference = new AtomicReference<>(user);
        boolean b = userAtomicReference.compareAndSet(user, user1);
        System.out.println(b);
        System.out.println(userAtomicReference.get());
    }
true
AtomicRef.User(a=22, b=23)

Last updated

Was this helpful?