深入理解CAS
1.什么是CAS?
CAS:比较当前工作内存中的值和主内存的值,如果这个值是期望的,那么则执行操作!,如果不是就一直循环。
缺点:
1.循环会耗时
2.一次性只能保证一个共享变量的原子性
3.ABA问题
CAS 是CPU的并发原语。
Unsafe类
2.ABA问题 (乐观锁)(狸猫换太子)AtomicInteger
//CAS compareAndSet比价并交换
public static void main(String[] args) {
//原子类
AtomicInteger atomicInteger = new AtomicInteger(2020);
//如果期望的值达到了,就更新,否则,就不更新
//CAS 是CPU的并发原语。
//对于我们平时写的SQL: 乐观锁
//============捣乱的线程===============
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2021, 2020));
System.out.println(atomicInteger.get());
//============期望的线程===============
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
}
3.原子引用 解决ABA问题(乐观锁) AtomicStampedReference
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
public class CASTest {
//Integer的缓存是 -128 到- 127 也就是说 超过这个范围会创建新的地址 ,地址不一样时,必然!= ,或者使用equals
//原子应用类,可以保证你在修改对象引用时的线程安全性,比较时可以按照偏移量进行
static AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(1, 1);
//CAS compareAndSet比价并交换
public static void main(String[] args) {
//原子类
//AtomicInteger atomicInteger = new AtomicInteger(2020);
//1.期望值 2.版本号
new Thread(()->{
int i = atomicInteger.getStamp(); //版本号
System.out.println(Thread.currentThread().getName() + i );
//进行一个修改 改变期望 ,并且3.是版本号 4.是改变后的版本号
atomicInteger.compareAndSet(1, 2, i, i+1);
System.out.println(Thread.currentThread().getName() + atomicInteger.getStamp() );
atomicInteger.compareAndSet(2, 1, i, i+1);
System.out.println(Thread.currentThread().getName() + atomicInteger.getStamp() );
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
int i = atomicInteger.getStamp(); //版本号
System.out.println(Thread.currentThread().getName() + i );
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicInteger.compareAndSet(1, 5 , i, i+1);
System.out.println(Thread.currentThread().getName() + atomicInteger.getStamp() );
},"B").start();
}
}
如果泛型是包装类,注意对象引用问题
Integer的缓存是 -128 到- 127 也就是说 超过这个范围会创建新的地址 ,地址不一样时,必然!= ,或者使用equals