ABA问题的产生
CAS会导致ABA问题
CAS算法实现一个重要前提需要去除内存中某时刻的数据并在当下时刻进行比较并替换,那么在这个时间差类会导致数据的变化,比如说一个线程one从内存位置V中取出A,这个时候另一个线程two也从内存中取出A,并且线程two进行了一些操作将值变成了B,然后线程two线程又将V位置的数据编程A,这时候线程one进行CAS操作发现内存中仍然是A,然后线程one操作成功。
尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。
原子引用
AtomicReferenceDemo
package com.liang.cas;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.concurrent.atomic.AtomicReference;
@AllArgsConstructor
@Data
class User{
private String username;
private String password;
}
public class AtomicReferenceDemo {
public static void main(String[] args) {
User user1 = new User("张三","123");
User user2 = new User("李四","123");
AtomicReference<User> atomicReference = new AtomicReference<>();
atomicReference.set(user1);
System.out.println(atomicReference.compareAndSet(user1, user2)+"\t"+atomicReference.get().toString());
System.out.println(atomicReference.compareAndSet(user1, user2)+"\t"+atomicReference.get().toString());
}
}
ABA 问题是怎么产生的
package com.liang.cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class ABADemo {
private static AtomicInteger atomicInteger = new AtomicInteger(100);
public static void main(String[] args) {
new Thread(()->{
atomicInteger.compareAndSet(100,101);
atomicInteger.compareAndSet(101,100);
},"one").start();
new Thread(()->{
try {
//保证上面的线程运行
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicInteger.compareAndSet(100,2020);
System.out.println(atomicInteger.get()); //2020
},"one").start();
}
}
当有一个值从 A 改为 B 又改为 A,这就是 ABA 问题。
时间戳原子引用
package com.liang.cas;
import java.util.concurrent.atomic.AtomicStampedReference;
public class ABADemo1 {
private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);
public static void main(String[] args) {
new Thread(() -> {
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + " 的版本号为:" + stamp);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(100, 101, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1 );
atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1 );
}).start();
new Thread(() -> {
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + " 的版本号为:" + stamp);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean b = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1);
System.out.println(b); // false
System.out.println(atomicStampedReference.getReference()); // 100
}).start();
}
}
修改失败
我们先保证两个线程的初始版本为一致,后面修改是由于版本不一样就会修改失败。