AtomicReference可以针对引用类型进行原子操作。
1. 构造
AtomicReference<Person> ar = new AtomicReference<>(new Person()); AtomicReference<Person> ar = new AtomicReference<>();
无参构造,相当于AutomicReference(null)
有参构造,初始化为initValue
2. 如果原来是A,则更新成B
public boolean compareAndSet(T source, T dest):source是改之前的值,Dest是改之后的值,source与当前真实值匹配了才能执行成功,返回值表示是否执行成功。
AtomicReference<Person> ar = new AtomicReference<>(new Person()); ar.compareAndSet(ar.get(), new Person(ar.get().getAge(), "newName"));
这里底层判断的时候是采用的内存地址相等判断法,而不是equals方法,所以在更新值的时候,是采用复制对象的方法,而不是在原有对象的基础上修改的方式。否则自旋的意义就没有了。
3. 取值并更新
V getAndSet(V newValue) 返回旧值
AtomicReference<Person> ar = new AtomicReference<>(new Person()); ar.getAndSet(new Person(ar.get().getAge(), "newName"));
4. 采用lambda表达式进行更新
V getAndUpdate(UnaryOperator<V> updateFunction) 返回旧值
V updateAndGet(UnaryOperator<V> updateFunction) 返回新值
AtomicReference<Person> ar = new AtomicReference<>(new Person()); System.out.println(ar.getAndUpdate(ele-> new Person(ele.getAge(), "newName"))); System.out.println(ar.updateAndGet(ele-> new Person(ele.getAge() + 5, "newName")));
此处需要注意,一定要基于原有的对象。
5. 基于二元操作数进行更新
V getAndAccumulate(V x, BinaryOperator<V> accumulatorFunction) 返回旧值
V accumulateAndGet(V x, BinaryOperator<V> accumulatorFunction) 返回新值
AtomicReference<Person> ar = new AtomicReference<>(new Person()); System.out.println(ar.getAndAccumulate(new Person(1, "temp"), (a, b) -> new Person(a.getAge() + b.getAge(), "newName"))); System.out.println(ar.accumulateAndGet(new Person(2, "temp"), (a, b) -> new Person(a.getAge() + b.getAge(), "newName")));
6. 直接更新值set()
AtomicReference<Person> ar = new AtomicReference<>(new Person()); ar.set(new Person(1, "temp"); System.out.println(ar.get());
7. 直接取值get()
AtomicReference的ABA的问题:
当初始值为A,在cas进行时,是查看当前是否是A,这个过程并不关心中间过程,只要当前值是A即可。所以,当中间有多次改动的时候,我们无法察觉。
AtomicStampedReference就是为了解决这个问题,他类似的给这个对象加了一个时间戳,表示对象的修改时间,这样每一次修改都会同步修改时间戳,这就可以保证时间戳是同步递增的,然后我们就知道A->B->C->A的改动了。
1. 构造
AtomicStampedReference<String> reference = new AtomicStampedReference<>("Hello", 1); System.out.println(reference.getReference()); System.out.println(reference.getStamp());
此处可以通过getReference取出值,getStamp取出时间戳
2. 如果原来是A,并且时间戳是a, 更新值为B,时间戳是b
AtomicStampedReference<String> reference = new AtomicStampedReference<>("Hello", 1);
reference.compareAndSet(reference.getReference(), "World", 2, 3); // 失败
reference.compareAndSet(reference.getReference(), "World", 1, 2); // 成功
必须值和时间戳同时满足,才可以更新
3. 直接修改值和标签set()
AtomicStampedReference<String> reference = new AtomicStampedReference<>("Hello", 1);
reference.set("World", reference.getStamp() + 1);
4. 尝试修改标签attemptStamp()
AtomicStampedReference<String> reference = new AtomicStampedReference<>("Hello", 1);
reference.attemptStamp(reference.getReference(), 2);
只有值匹配的时候,才可以修改标签