AtomicReference类提供了一个可以原子读写的对象引用变量。 原子意味着尝试更改相同AtomicReference的多个线程(例如,使用比较和交换操作)不会使AtomicReference最终达到不一致的状态。 AtomicReference甚至有一个先进的compareAndSet()方法,它可以将引用与预期值(引用)进行比较,如果它们相等,则在AtomicReference对象内设置一个新的引用。
一、AtomicReference
通过volatile和Unsafe提供的CAS函数实现原子操作。 自旋+CAS的无锁操作保证共享变量的线程安全
- value是volatile类型,这保证了:当某线程修改value的值时,其他线程看到的value的值都是最新的值,即修改之后的volatile的值
- 通过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