08 April 2019

[TOC]

AtomicReference

作用

原子类(CAS+volatile)的引入是为了可以用一种类似乐观锁的方式操作共享资源,而不用依靠悲观锁模式

说明

1.value是volatile类型。这保证了:当某线程修改value的值时,其他线程看到的value值都是最新的value值,即修改之后的volatile的值。

2.通过CAS设置value。这保证了:当某线程池通过CAS函数(如compareAndSet函数)设置value时,它的操作是原子的,即线程在操作value时不会被中断。

CAS的缺点

  • 循环时间长,开销大
  • 只能保证一个共享变量的操作
  • ABA问题

示例

import java.util.concurrent.atomic.AtomicReference;

public class AtomicReferenceTest {
    
    public static void main(String[] args){

        // 创建两个Person对象,它们的id分别是101和102。
        Person p1 = new Person(101);
        Person p2 = new Person(102);
        // 新建AtomicReference对象,初始化它的值为p1对象
        AtomicReference ar = new AtomicReference(p1);
        // 通过CAS设置ar。如果ar的值为p1的话,则将其设置为p2。
        ar.compareAndSet(p1, p2);

        Person p3 = (Person)ar.get();
        System.out.println("p3 is "+p3);
        System.out.println("p3.equals(p1)="+p3.equals(p1));
    }
}

class Person {
    volatile long id;
    public Person(long id) {
        this.id = id;
    }
    public String toString() {
        return "id:"+id;
    }
}
p3 is id:102
p3.equals(p1)=false

结果说明

新建AtomicReference对象ar时,将它初始化为p1。

1.紧接着,通过CAS函数对它进行设置。如果ar的值为p1的话,则将其设置为p2。

2.最后,获取ar对应的对象,并打印结果。p3.equals(p1)的结果为false,这是因为Person并没有覆盖equals()方法,而是采用继承自Object.java的equals()方法;而Object.java中的equals()实际上是调用”==”去比较两个对象,即比较两个对象的地址是否相等。

  • 为什么要重写equals和hashcode

    例如hashmap采用自定义的对象作为key,如果不重写hashcode和equals的时候,取落点和链表equals的比较都是内存地址的比较。

ABA问题解决

AtomicStampedReference就是上面所说的加了版本号的AtomicReference。

public class AtomicStampedReference<V> {

    private static class Pair<T> {
        final T reference;
        final int stamp;
        private Pair(T reference, int stamp) {
            this.reference = reference;
            this.stamp = stamp;
        }
        static <T> Pair<T> of(T reference, int stamp) {
            return new Pair<T>(reference, stamp);
        }
    }

    private volatile Pair<V> pair;
}


blog comments powered by Disqus