您当前的位置: 首页 >  Java
  • 0浏览

    0关注

    1477博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

java并发编程(6)--CAS会导致“ABA问题“ 解决方案

软件工程小施同学 发布时间:2021-02-07 18:39:40 ,浏览量:0

一、⾼频⾯试题

1. 原⼦类AtomicInteger的ABA问题谈谈?原⼦更新引⽤你知道吗?

2. 我们知道ArrayList是线程不安全,请编码写⼀个不安全的案例并给出解决⽅案

3. 公平锁/⾮公平锁/可重⼊锁/递归锁/⾃旋锁谈谈你的理解?请⼿写⼀个⾃旋锁

4. CountDownLath/CyclicBarrier/Semaphore使⽤过吗?

5. 阻塞队列知道吗?

6. 线程池⽤过吗?ThreadPoolExecutor谈谈你的理解?⽣产上你如何设置合理参数

7. 死锁编码及定位分析

 

二、ABA问题

所谓ABA问题,就是CAS算法实现需要取出内存中某时刻的数据并在当下时刻⽐较并替换,这⾥存在⼀ 个时间差,那么这个时间差可能带来意想不到的问题。

⽐如,⼀个线程B 从内存位置Value中取出2,这时候另⼀个线程A 也从内存位置Value中取出2,并且 线程A 进⾏了⼀些操作将值变成了5,然后线程A ⼜再次将值变成了2,这时候线程B 进⾏CAS操作发现 内存中仍然是2,然后线程B 操作成功。

尽管线程B 的CAS操作成功,但是不代表这个过程就是没有问题的。

有这样的需求,⽐如CAS,只注重头和尾,只要⾸尾⼀致就接受。

但是有的需求,还看重过程,中间不能发⽣任何修改,这就引出了 AtomicReference 原⼦引 ⽤。

 

三、AtomicReference原⼦引⽤

AtomicInteger对整数进⾏原⼦操作,如果是⼀个POJO呢?可以⽤AtomicReference来包装这个 POJO,使其操作原⼦化。

package thread;

public class AtomicReferenceDemo {

    public static void main(String[] args) {

        User user1 = new User("Jack",25);
        User user2 = new User("Tom",21);

        AtomicReference atomicReference = new AtomicReference();

        atomicReference.set(user1);

        System.out.println(atomicReference.compareAndSet(user1,user2)+"\t"+atomicReference.get()); // true

        System.out.println(atomicReference.compareAndSet(user1,user2)+"\t"+atomicReference.get()); //false

    }
}

 

四、ABA问题的解决(AtomicStampedReference 类似于时间 戳)
ThreadA    100 1             2020 2

ThreadB    100 1    111 2    100 3

使⽤AtomicStampedReference类可以解决ABA问题。

这个类维护了⼀个“版本号”Stamp,在进⾏CAS操作的时候,不仅要⽐较当前值,还要⽐较版本号。

只有两者都相等,才执⾏更新操作。

 

解决ABA问题的关键⽅法:

参数说明:

  • V expectedReference, 预期值引⽤
  • V newReference, 新值引⽤
  • int expectedStamp, 预期值时间戳
  • int newStamp, 新值时间戳
package thread;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

public class ABADemo {

    static AtomicReference atomicReference = new AtomicReference(100);

    static AtomicStampedReference atomicStampedReference = new AtomicStampedReference(100, 1);

    public static void main(String[] args) {

        System.out.println("======ABA问题的产⽣======");

        // 100->101->100
        new Thread(() -> {

            atomicReference.compareAndSet(100, 101);
            atomicReference.compareAndSet(101, 100);

        }, "t1").start();


        // 100->2020
        new Thread(() -> {

            try {
                TimeUnit.SECONDS.sleep(1);
            }
            catch (InterruptedException e) {

                e.printStackTrace();

            }

            System.out.println(atomicReference.compareAndSet(100, 2020) + "\t" + atomicReference.get().toString());

        }, "t2").start();


        try {

            TimeUnit.SECONDS.sleep(2);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }


        System.out.println(" ======ABA问题的解决======");


        // 100(1)->101(2)->100(3)
        new Thread(() -> {

            int stamp = atomicStampedReference.getStamp();

            // t3	第⼀次 版本号: 1
            System.out.println(Thread.currentThread().getName() + "\t第⼀次 版本号: " + stamp);

            try {
                TimeUnit.SECONDS.sleep(1);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }



            //t3	第⼆次 版本号: 2
            atomicStampedReference.compareAndSet(100,101, atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); System.out.println(Thread.currentThread().getName() + "\t第⼆次 版本号: " + atomicStampedReference.getStamp());


            //t3	第三次 版本号: 3
            atomicStampedReference.compareAndSet(101,100, atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); System.out.println(Thread.currentThread().getName() + "\t第三次 版本号: " + atomicStampedReference.getStamp());

        }, "t3").start();


        //
        new Thread(() -> {

            int stamp = atomicStampedReference.getStamp();

            // stamp = 1
            // t4	第⼀次 版本号: 1
            System.out.println(Thread.currentThread().getName() + "\t第⼀次 版本号: " + stamp);

            try {
                TimeUnit.SECONDS.sleep(3);

            }
            catch (InterruptedException e) {

                e.printStackTrace();

            }

            boolean result=atomicStampedReference.compareAndSet(100,2020, stamp,stamp+1);

            // t4	修改成功 与否:false 当前最新版本号3
            System.out.println(Thread.currentThread().getName()+"\t修改成功 与否:"+result+" 当前最新版本号"+atomicStampedReference.getStamp());

            // t4	当前实际值:100
            System.out.println(Thread.currentThread().getName()+"\t当前实际值:"+atomicStampedReference.getReference());

        }, "t4").start();

    }

}

 

 

 

 

 

 

 

 

关注
打赏
1665320866
查看更多评论
立即登录/注册

微信扫码登录

0.0648s