• Java之juc旅途-atomic(五)


    Atomic原子类就是利用自旋+CAS来保证线程安全的。有以下几个基础类:

    • AtomicBoolean
    • AtomicInteger
    • AtomicLong
    • AtomicReference
    • AtomicStampedReference
    • AtomicMarkableReference

    它还维护了一些保证原子性修改元素的集合,不过一般用不上:

    • AtomicIntegerArray
    • AtomicLongArray

    甚至还可以保证对象的基础类型的原子性修改:

    • AtomicIntegerFieldUpdater
    • AtomicLongFieldUpdater

    用AtomicInteger来举例子,本质就是用volatile来维护一个值。

    public class AtomicInteger extends Number implements java.io.Serializable {
        private static final long serialVersionUID = 6214790243416807050L;
    
        private volatile int value;
        
        public AtomicInteger(int initialValue) {
            value = initialValue;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    但是volatile只能保证这个变量的可见性,不能保证他的原子性,所以在操作的时候,用到了UnSafe类。
    可以看看getAndIncrement这个类似i++的函数,可以发现,是调用了UnSafe中的getAndAddInt。

     public final int getAndIncrement() {
            return U.getAndAddInt(this, VALUE, 1);
     }
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    再深入看,就发现是一个 自旋+CAS了,在这个过程中,通过compareAndSwapInt比较更新value值,如果更新失败,重新获取旧值,然后更新,这样就保证了原子性的增加了。

     public final int getAndAddInt(Object o, long offset, int delta) {
            int v;
            do {
                v = getIntVolatile(o, offset);
            } while (!weakCompareAndSetInt(o, offset, v, v + delta));
            return v;
        }
    
     public final boolean weakCompareAndSetInt(Object o, long offset,
                                                  int expected,
                                                  int x) {
            return compareAndSetInt(o, offset, expected, x);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    CAS相对于其他锁,不会进行内核态操作,有着一些性能的提升。但同时引入自旋,当锁竞争较大的时候,自旋次数会增多。cpu资源会消耗很高。
    换句话说,CAS+自旋适合使用在低并发有同步数据的应用场景。

    但是,有一点需要注意的是,大部分的原子类都是不能解决ABA的问题的,所以新加了一个AtomicStampedReference类,它会维护一个Pair,然后Pair里会携带类似版本的字段stemp,在CAS额外判断stemp的值来解决ABA问题:

    public boolean compareAndSet(V   expectedReference,
                                     V   newReference,
                                     int expectedStamp,
                                     int newStamp) {
            Pair<V> current = pair;
            return
                expectedReference == current.reference &&
                expectedStamp == current.stamp &&
                ((newReference == current.reference &&
                  newStamp == current.stamp) ||
                 casPair(current, Pair.of(newReference, newStamp)));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    Mysql读写分离
    ChatGPT Prompting开发实战(四)
    scrapy框架-Middleware(爬虫中间件)
    mysql数据库root密码忘记了,这里有一个简单的方法可以解决
    Vue的使用(1)
    Codeforces Round 895 (Div. 3)
    ES6 入门教程 3 变量的解构赋值 3.3 字符串的解构赋值 & 3.4 数值和布尔值的解构赋值 ~ 3.7 用途
    分享个人收集或整理的word中常用的vba代码
    前端进击笔记第十九节 Angular,React,Vue 三大前端框架的设计特色
    双飞翼布局和圣杯布局
  • 原文地址:https://blog.csdn.net/h295928126/article/details/126024570