1、ABA问题
使用AtomicReference
的变量在修改变量之前,感知不到其他线程对变量的修改。
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.atomic.AtomicReference;
@Slf4j
public class ABA问题 {
static AtomicReference<String> ref = new AtomicReference<>("A");
public static void main(String[] args) throws InterruptedException {
log.debug("mian start ......");
//获取A的值
//这个共享变量被其他线程修改过
String prev = ref.get();
other();
Thread.sleep(1000);
//尝试改为C
log.debug("change A -> C {}",ref.compareAndSet(prev,"C"));
}
public static void other() throws InterruptedException {
new Thread(() -> {
log.debug("change A -> B {}",ref.compareAndSet(ref.get(),"B"));
},"t1").start();
Thread.sleep(500);
new Thread(() -> {
log.debug("change B -> A {}",ref.compareAndSet(ref.get(),"A"));
},"t2").start();
}
}
输出:
19:17:45.888 [main] DEBUG ABA问题 - mian start ......
19:17:45.968 [t1] DEBUG ABA问题 - change A -> B true
19:17:46.477 [t2] DEBUG ABA问题 - change B -> A true
19:17:47.485 [main] DEBUG ABA问题 - change A -> C true
2、解决ABA问题(一)
使用AtomicStampedReference
解决ABA问题,使用了版本号解决。
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.atomic.AtomicStampedReference;
@Slf4j
public class 解决ABA问题 {
static AtomicStampedReference<String> ref = new AtomicStampedReference<>("A",0);
public static void main(String[] args) throws InterruptedException {
log.debug("mian start ......");
//获取A的值
//这个共享变量被其他线程修改过
String prev = ref.getReference();
//获取版本号
int stamp = ref.getStamp();
other();
Thread.sleep(1000);
//尝试改为C
log.debug("change A -> C {}",ref.compareAndSet(prev,"C",stamp,stamp + 1));
}
public static void other() throws InterruptedException {
new Thread(() -> {
int stamp = ref.getStamp();
log.debug("change A -> B {}",ref.compareAndSet(ref.getReference(),"B",stamp,stamp+1));
},"t1").start();
Thread.sleep(500);
new Thread(() -> {
int stamp = ref.getStamp();
log.debug("change B -> A {}",ref.compareAndSet(ref.getReference(),"A",stamp,stamp+1));
},"t2").start();
}
}
解决:
20:51:38.275 [main] DEBUG ABA问题 - mian start ......
20:51:38.347 [t1] DEBUG ABA问题 - change A -> B true
20:51:38.847 [t2] DEBUG ABA问题 - change B -> A true
20:51:39.847 [main] DEBUG ABA问题 - change A -> C false
解决ABA问题(二)
如果只是期望知道主线程在修改之前有没有被别的线程修改过,使用AtomicMarkableReference
即可实现。
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.atomic.AtomicMarkableReference;
@Slf4j
public class ABA问题2 {
public static void main(String[] args) throws InterruptedException {
GarbageBag bag = new GarbageBag("装满了垃圾");
AtomicMarkableReference<GarbageBag> ref = new AtomicMarkableReference<>(bag,true);
log.debug("start...");
GarbageBag prev = ref.getReference();
log.debug(prev.toString());
new Thread(() -> {
log.debug("start....");
bag.setDesc("空垃圾袋");
ref.compareAndSet(bag,bag,true,false);
log.debug(bag.toString());
}).start();
Thread.sleep(1000);
log.debug("想换一只新塑料袋?");
boolean success = ref.compareAndSet(prev, new GarbageBag("空塑料袋"), true, false);
log.debug("换了么? {}",success);
log.debug(ref.getReference().toString());
}
}
class GarbageBag{
private String desc;
public GarbageBag(String desc){
this.desc = desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "GarbageBag{" +
"desc='" + desc + '\'' +
'}';
}
}
输出:
00:17:59.983 [main] DEBUG ABA问题2 - start...
00:17:59.988 [main] DEBUG ABA问题2 - GarbageBag{desc='装满了垃圾'}
00:18:00.062 [Thread-0] DEBUG ABA问题2 - start....
00:18:00.063 [Thread-0] DEBUG ABA问题2 - GarbageBag{desc='空垃圾袋'}
00:18:01.063 [main] DEBUG ABA问题2 - 想换一只新塑料袋?
00:18:01.063 [main] DEBUG ABA问题2 - 换了么? false
00:18:01.065 [main] DEBUG ABA问题2 - GarbageBag{desc='空垃圾袋'}