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?