• ABA问题是什么?以及相关解决办法。


    2.ABA问题

    我们先看个例子:

    public class Demo3 {
    	static AtomicReference<String> str = new AtomicReference<>("A");
    	public static void main(String[] args) {
    		new Thread(() -> {
    			String pre = str.get();
    			System.out.println("change");
    			try {
    				other();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			try {
    				Thread.sleep(1000);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
                //主线程
    			//把str中的A改为C
    			System.out.println("change A->C " + str.compareAndSet(pre, "C"));
    		}).start();
    	}
    
    	static void other() throws InterruptedException {
    		//线程1
            new Thread(()-> {
    			System.out.println("change A->B " + str.compareAndSet("A", "B"));
    		}).start();
    		Thread.sleep(500);
            //线程二
    		new Thread(()-> {
    			System.out.println("change B->A " + str.compareAndSet("B", "A"));
    		}).start();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    如例子所示,主线程获取完原子引用里的数值A,进入休眠:之后

    • 线程一把A改成了B
    • 线程二把B又改成了A

    此时,主线程结束休眠,但是对之前线程一和线程二的操作毫不知情。但是还能正常运行。

    这就是ABA问题,其实这种情况在大多数场景里都不会引发问题,但是我们还是应该规范下,让主线程知道自己的被人修改了。并且不认可结果。

    于是便引出了接下来的类。

    3.AtomicStampedReference

    这个类为上面的问题提供了解决办法–加入版本号

    public class Demo3 {
    	//指定版本号
    	static AtomicStampedReference<String> str = new AtomicStampedReference<>("A", 0);
    	public static void main(String[] args) {
    		new Thread(() -> {
    			String pre = str.getReference();
    			//获得版本号
    			int stamp = str.getStamp();
    			System.out.println("change");
    			try {
    				other();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			try {
    				Thread.sleep(1000);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			//把str中的A改为C,并比对版本号,如果版本号相同,就执行替换,并让版本号+1
    			System.out.println("change A->C stamp " + stamp + str.compareAndSet(pre, "C", stamp, stamp+1));
    		}).start();
    	}
    
    	static void other() throws InterruptedException {
    		new Thread(()-> {
    			int stamp = str.getStamp();
    			System.out.println("change A->B stamp " + stamp + str.compareAndSet("A", "B", stamp, stamp+1));
    		}).start();
    		Thread.sleep(500);
    		new Thread(()-> {
    			int stamp = str.getStamp();
    			System.out.println("change B->A stamp " + stamp +  str.compareAndSet("B", "A", stamp, stamp+1));
    		}).start();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    AtomicStampedReference 可以给原子引用加上版本号,追踪原子引用整个的变化过程,如: A -> B -> A -> C ,通过AtomicStampedReference,我们可以知道,引用变量中途被更改了几次,版本号相同才会执行主线程操作。

    但是有时候,并不关心引用变量更改了几次,只是单纯的关心是否更改过,所以就有了 AtomicMarkableReference

    4.AtomicMarkableReference

    public class Demo4 {
    	//指定版本号
    	static AtomicMarkableReference<String> str = new AtomicMarkableReference<>("A", true);
    	public static void main(String[] args) {
    		new Thread(() -> {
    			String pre = str.getReference();
    			System.out.println("change");
    			try {
    				other();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			try {
    				Thread.sleep(1000);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			//把str中的A改为C,并比对版本号,如果版本号相同,就执行替换,并让版本号+1
    			System.out.println("change A->C mark " +  str.compareAndSet(pre, "C", true, false));
    		}).start();
    	}
    
    	static void other() throws InterruptedException {
    		new Thread(() -> {
    			System.out.println("change A->A mark " + str.compareAndSet("A", "A", true, false));
    		}).start();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    两者的区别

    • AtomicStampedReference 需要我们传入整型变量作为版本号,来判定是否被更改过
    • AtomicMarkableReference需要我们传入布尔变量作为标记,来判断是否被更改过
  • 相关阅读:
    AJAX以及Axios框架
    录屏软件用什么好?直播会议用什么录屏软件?
    第一章 CIS 安全基准-Ingress配置证书
    dubbo-admin配置及使用
    VulnStack - ATT&CK红队评估实战(四) Writeup
    第58篇:创建Nios II工程之Hello_World<四>
    机器人控制算法九之 位姿描述与空间变换
    程序环境和预处理
    狂神。SpringBoot学习(3)
    CPT205-Computer Graphics
  • 原文地址:https://blog.csdn.net/abaidaye/article/details/126282158