AtomicReference和AtomicStampedReference

AtomicReference类提供了一个可以原子读写的对象引用变量。 原子意味着尝试更改相同AtomicReference的多个线程(例如,使用比较和交换操作)不会使AtomicReference最终达到不一致的状态。 AtomicReference甚至有一个先进的compareAndSet()方法,它可以将引用与预期值(引用)进行比较,如果它们相等,则在AtomicReference对象内设置一个新的引用。

一、AtomicReference

通过volatile和Unsafe提供的CAS函数实现原子操作。 自旋+CAS的无锁操作保证共享变量的线程安全

  1. value是volatile类型,这保证了:当某线程修改value的值时,其他线程看到的value的值都是最新的值,即修改之后的volatile的值
  2. 通过CAS设置value。这保证了:某线程池通过CAS函数(如compareAndSet函数)设置value时,它的操作时原子性的,即线程在操作vu略时不会被中断。

但是CAS操作可能存在ABA问题。AtomicStampedReference的出现就是为了解决这问题

备注:ABA问题:原始值为A,某个线程修改为B,另一个线程又修改为A,当第三个线程访问时,获取的值依然是A,认为该值没有被修改过。

注意:如下代码是线程不安全的

//先获取原始值
Integer oldValue = atomicReference.get();
//根据原始值比较进行修改
atomicReference.compareAndSet(oldValue,oldValue+1);

若要保证线程安全,需要加锁(Lock或者synchronized)

Lock lock = new ReentrantLock();
...
 lock.lock();
 Integer oldValue = atomicReference.get();
 atomicReference.compareAndSet(oldValue,oldValue+1);
lock.unlock();

也就是说对于AtomicXX类,单个方法是原子性的,但是同时调用多个方法则不是原子性的,需要加锁实现线程安全。

二、AtomicStampedReference

AtomicStampedReference它内部不仅维护了对象值,还维护了一个时间戳(我这里把它称为时间戳,实际上它可以是任何一个整数,它使用整数来表示状态值)。当AtomicStampedReference对应的数值被修改时,除了更新数据本身外,还必须要更新时间戳。当AtomicStampedReference设置对象值时,对象值以及时间戳都必须满足期望值,写入才会成功。因此,即使对象值被反复读写,写回原值,只要时间戳发生变化,就能防止不恰当的写入。

举个栗子

AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference(0,1);
  lock.lock();
 Integer oldValue = atomicStampedReference.getReference();//0
 int stamp = atomicStampedReference.getStamp();//1
 atomicStampedReference.compareAndSet(oldValue,oldValue+1, stamp,stamp+1);
 lock.unlock();
 System.out.println(atomicStampedReference.getReference());//1
 System.out.println(atomicStampedReference.getStamp());//2
posted @ 2019-12-16 17:22  炫舞风中  阅读(571)  评论(0编辑  收藏  举报