美文网首页
AtomicInteger 类

AtomicInteger 类

作者: gczxbb | 来源:发表于2020-01-10 01:03 被阅读0次

    自增操作符(++),非原子性,线程不安全。线程安全的计数采用 synchronized 或 AtomicInteger 类。

    AtomicInteger 类的 getAndIncrement() 方法实现原子操作,基于cas算法,非阻塞同步。

    CAS算法

    悲观锁,
    每次请求数据时,都会认为其他线程修改,每次都会上锁,等用完后,解锁让其他线程使用,synchronized 和ReentrantLock 都是悲观锁。

    乐观锁,
    比较乐观的认为其他线程不会修改数据,不会上锁,在更新时,会判断有无其他线程更新数据,版本号机制或CAS算法实现,当线程拿到数据时,同时拿到version,更新数据且version++,写回时,发现另一个线程已经改新了数据,并提高了version,则会更新失败的,避免两个线程操作数据相互覆盖。

    CAS算法
    比较与交换,是一种非阻塞同步,读写的内存V,比较的值A,新值B,多个线程并发操作时,只V的值是A,才会改成B,这一套比较并修改是原子操作,其他线程发现已经是B了,不是A,不修改,会自旋。
    在FutureTask中经常见到compareAndSwapInt。

    算法问题,ABA问题,自旋,cpu开销,只能保证一个共享变量的原子操作。
    ABA问题,
    比较初始值A检查时,是A,没问题,可能中间被改动过,又改回了A,会认为没改过。AtomicStampedReference解决,控制变量值的版本解决。
    乐观锁在读比较多时适用。atomic。synchronized使用写多场景。

    i++,线程先取i值,然后+1,最后存入,一个线程未存入前,另一个线程取到原值,两个线程i++写入对值将相同,两次自增,算作一次。

    int count=0;
        for (int i = 0; i < 10; i++) {
            new Thread() {
                public void run() {
                    for (int j = 0; j < 1000; j++) {
                        count++;
                        atomicInteger.getAndIncrement();
                        countVolatile++;
                    }
                }
            }.start();
        }
    

    10个线程并发操作count变量,主线程中定义,初始值是0,对count操作时,拷贝副本到线程工作内存,加1,存入主内存。如果是volatile修饰:线程的每次操作count都会有这些步骤,使用时都取新的,保证使用其他线程对count的修改的最新值,更新后,都会写入主内存,确保其他线程可见。
    如果没有volatile修饰,线程连续多次操作count时,不会每次都写入或读取最新,估计连续很多次做一次读写操作。总之,count的自增操作不是原子性。
    当一个线程读,还未写入时,另一个线程进入,获取主线程当旧值,两个线程当操作是在相同值的基础上进行,如果都只加一次就写入,少算一次自增。volatile可保证可见性,不能保证原子性。最后的值将小于10000。

     count:9252
     AtomicInteget count:10000
     countVolatile:9027
    

    非阻塞同步

    AtomicInteger 类的 getAndIncrement() 方法实现原理是非阻塞同步。

    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }
    

    内部使用Unsafe类。

    public final int getAndAddInt(Object var1, long var2, int var4) {
            int var5;
            do {
                var5 = this.getIntVolatile(var1, var2);
            } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    
            return var5;
    }
    

    每次 getAndAddInt() 调用,都会有一个 while 循环,取var2位置的值var5,
    compareAndSwapInt是native方法,原子性。当调用它时,会比较当前var2位置是否是var5,如果还是var5,说明其他线程未更新,将var5+var4,var4就是1,更新后当值写入var2位置。该操作是原子操作,保证线程安全。

    如果这时已经不是var5,说明有线程已经更新值,返回false,继续循环,再去取值,这个过程并没有线程休眠,因此是非阻塞并发。


    任重而道远

    相关文章

      网友评论

          本文标题:AtomicInteger 类

          本文链接:https://www.haomeiwen.com/subject/jiwdictx.html