CAS 및 원자 참조에 대한 깊은 이해
하나, CAS에 대한 심층적 이해
CAS 란?
ublic class CASDemo {
//CAS compareAndSet:比较并交换
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
// 期望、更新
// public final boolean compareAndSet(int expect, int update)
// 如果我期望的值达到了,那么就更新,否则,就不更新, CAS 是CPU的并发原语!
// ============== 捣乱的线程 ==================
System.out.println(atomicInteger.compareAndSet(2020,2021));
System.out.println(atomicInteger.get());
//atomicInteger.getAndIncrement();
System.out.println(atomicInteger.compareAndSet(2021,2020));
System.out.println(atomicInteger.get());
// ============== 期望的线程 ==================
System.out.println(atomicInteger.compareAndSet(2020,6666));
System.out.println(atomicInteger.get());
}
}
안전하지 않은 수업
위 그림 뒤의 워드 연산 메모리
CAS : 현재 작업 메모리의 값과 메인 메모리의 값을 비교하여이 값이 예상되면 작업을 수행하십시오. 그렇지 않으면 계속 반복하십시오!
단점 :
1.주기는 시간이 많이 걸립니다
2. 한 번만 공유 변수의 원 자성을 보장 할 수 있습니다.
3. ABA 문제
CAS : ABA 문제 (왕자를위한 사향 고양이)
public class CASDemo {
// CAS compareAndSet 比较并交换
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
// 期望、更新
// public final boolean compareAndSet(int expect, int update)
// 如果期望得值成功了,那么就更新,否则就不更新 CAS是CPU的并发原语。
// ============== 捣乱的线程 ==================
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, 6666));
System.out.println(atomicInteger.get());
}
}
두 원자 인용
ABA 문제를 해결하고 원자 참조를 도입하십시오! 해당 생각 : 낙관적 잠금!
버전 번호로 원자 작동!
public class CASDemo {
// AtomicStampedReference 注意,如果泛型是一个包装类,注意对象的引用问题,范围内用同一个,超出自动new新的
// 正常在业务操作,这里面比较的都是一个个对象
static AtomicStampedReference<Integer> atomicStampedReference = new
AtomicStampedReference<>(1, 1);
// CAS compareAndSet : 比较并交换!
public static void main(String[] args) {
new Thread(() -> {
int stamp = atomicStampedReference.getStamp(); // 获得版本号
System.out.println("a1=>" + stamp);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(1, 2,
atomicStampedReference.getStamp(),
atomicStampedReference.getStamp() + 1);
System.out.println("a2=>" + atomicStampedReference.getStamp());
System.out.println(atomicStampedReference.compareAndSet(2, 1,
atomicStampedReference.getStamp(),
atomicStampedReference.getStamp() + 1));
System.out.println("a3=>" + atomicStampedReference.getStamp());
}, "a").start();
// 乐观锁的原理相同!
new Thread(() -> {
int stamp = atomicStampedReference.getStamp(); // 获得版本号
System.out.println("b1=>" + stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicStampedReference.compareAndSet(1, 6,
stamp, stamp + 1));
System.out.println("b2=>" + atomicStampedReference.getStamp());
}, "b").start();
}
}
노트:
Integer는 개체 캐싱 메커니즘을 사용하며 기본 범위는 -128 ~ 127이며 valueOf는 캐싱을 사용하므로 new 대신 개체 인스턴스를 얻기 위해 정적 팩토리 메서드 valueOf를 사용하는 것이 좋습니다. new는 확실히 새 개체를 만들고 새 메모리를 할당합니다. 우주;