CAS与原子引用涉及到JVM以及更底层的一些知识,笔者会在JVM篇中写的更细致一点
CAS 是Java设置的CPU的并发原语
Java是无法直接操作内存的
但Java可以调用C++
而C++可以操作内存
Java可以通过native类调用C++来操作内存
CAS = Compare And Set 比较并交换
CAS是:比较当前内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作,如果不是就一直循环比较(自循环)
缺点:
1、自循环会耗时
2、一次性职能保证一个共享变量的原子性
3、会存在ABA问题
/**
* Atomically sets the value to newValue
* if the current value code == expectedValue,
* with memory effects as specified by VarHandle#compareAndSet.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expectedValue, int newValue) {
return U.compareAndSetInt(this, VALUE, expectedValue, newValue);
}
package CAS;
import java.util.concurrent.atomic.AtomicInteger;
public class casDemo {
// CAS 是CPU的并发原语
// compareAndSet:比较并交换
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
// public final boolean compareAndSet(int expectedValue, int newValue)
// expectedValue:期望值 newValue:更新值
// 比较,如果期望值达到了,则变为更新值,否则就不跟新
atomicInteger.compareAndSet(2020,2023);
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2020, 2023));
}
}
带版本号的原子操作 解决ABA问题! 与乐观锁很相似!
package CAS;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
public class casDemo02 {
// AtomicStampedReference 注意,如果泛型时一个包装类,注意对象的引用问题
public static void main(String[] args) {
AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(1,1);
new Thread(()->{
// 获得目前的版本号(时间戳)
int stamp = atomicInteger.getStamp();
System.out.println("A1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(atomicInteger.compareAndSet(1, 2, atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
System.out.println("A2=>"+atomicInteger.getStamp());
System.out.println(atomicInteger.compareAndSet(2, 1, atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
System.out.println("A3=>"+atomicInteger.getStamp());
},"A").start();
new Thread(()->{
// 获得目前的版本号(时间戳)
int stamp = atomicInteger.getStamp();
System.out.println("B1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(atomicInteger.compareAndSet(1, 6, stamp, stamp + 1));
System.out.println("B1=>"+atomicInteger.getStamp());
},"B").start();
}
}
注意:
Integer使用了对象缓存机制,默认范围是-128~127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valuOf使用缓存,而new一定会创建新的对象分配新的内存空间;
在示例中,我们一开始使用的expectedRefrence 和 newReference 都是大于127的且使用new获取对象,因此,内存中创造了新的对象分配新的空间,所以使用cas时一直为false