java中Atomic变量的实现原理是怎么样的( 三 )


2.1原子变量类使用既然我们上面也说到了,使用Synchronized锁有点小题大作了,我们用原子变量类来改一下:
class Count{ // 共享变量(使用AtomicInteger来替代Synchronized锁) private AtomicInteger count = new AtomicInteger(0); public Integer getCount() { return count.get(); } public void increase() { count.incrementAndGet(); }}// Main方法还是如上修改完,无论执行多少次,我们的结果永远是100!
其实Atomic包下原子类的使用方式都不会差太多,了解原子类各种类型,看看API,基本就会用了(网上也写得比较详细,所以我这里果断偷懒了)...
2.2ABA问题使用CAS有个缺点就是ABA的问题,什么是ABA问题呢?首先我用文字描述一下:
现在我有一个变量count=10,现在有三个线程,分别为A、B、C线程A和线程C同时读到count变量,所以线程A和线程C的内存值和预期值都为10此时线程A使用CAS将count值修改成100修改完后,就在这时,线程B进来了,读取得到count的值为100(内存值和预期值都是100),将count值修改成10线程C拿到执行权,发现内存值是10,预期值也是10,将count值修改成11上面的操作都可以正常执行完的,这样会发生什么问题呢??线程C无法得知线程A和线程B修改过的count值,这样是有风险的。
下面我再画个图来说明一下ABA的问题(以链表为例):

java中Atomic变量的实现原理是怎么样的


2.3解决ABA问题要解决ABA的问题,我们可以使用JDK给我们提供的AtomicStampedReference和AtomicMarkableReference类。
AtomicStampedReference:
An {@code AtomicStampedReference} maintains an object referencealong with an integer "stamp", that can be updated atomically.简单来说就是在给为这个对象提供了一个版本,并且这个版本如果被修改了,是自动更新的。
原理大概就是:维护了一个Pair对象,Pair对象存储我们的对象引用和一个stamp值。每次CAS比较的是两个Pair对象
// Pair对象 private static class Pair\u0026lt;T\u0026gt; { final T reference; final int stamp; private Pair(T reference, int stamp) { this.reference = reference; this.stamp = stamp; } static \u0026lt;T\u0026gt; Pair\u0026lt;T\u0026gt; of(T reference, int stamp) { return new Pair\u0026lt;T\u0026gt;(reference, stamp); } } private volatile Pair\u0026lt;V\u0026gt; pair; // 比较的是Pari对象 public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) { Pair\u0026lt;V\u0026gt; current = pair; return expectedReference == current.reference \u0026amp;\u0026amp; expectedStamp == current.stamp \u0026amp;\u0026amp; ((newReference == current.reference \u0026amp;\u0026amp; newStamp == current.stamp) || casPair(current, Pair.of(newReference, newStamp))); }因为多了一个版本号比较,所以就不会存在ABA的问题了。
2.4LongAdder性能比AtomicLong要好如果是 JDK8,推荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数)。去查阅了一些博客和资料,大概的意思就是:


推荐阅读