JAVA并发编程高级——原子变量操作类(AtomicLong)

原子变量操作类

        JUC 并发包中包含有 AtomicInteger、AtomicLong 和 AtomicBoolean 等原子性操作类,它们的原理类似,本文讲解 AtomicLong类。Atomicong 是原子性递增或者递减类,其内部使用 Unsafe 来实现,我们看下面的代码。

public class AtomicLong extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 1927816293512124184L;
    //(1)获取Unsafe实例
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    //(2)存放变量value的偏移量
    private static final long valueOffset;
    //(3)判断JVM是否支持Long类型无锁CAS
    static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();

    private static native boolean VMSupportsCS8();

    static {
        try {
            //(4)获取value在ATomicLong中的偏移量
            valueOffset = unsafe.objectFieldOffset
                (AtomicLong.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
    
    //(5)实际变量值
    private volatile long value;

    public AtomicLong(long initialValue) {
        value = initialValue;
    }
}

以上是AtomicLong的源码

        代码(1)通过 Unsafe.getUnsafe()方法获取到 Unsafe 类的实例,这里你可能会有疑问,为何能通过 Unsafe.getUnsafe()方法获取到 Unsafe 类的实例?其实这是因为 AtomicLong类也是在 rt.jar 包下面的,AtomicLong 类就是通过 BootStarp 类加载器进行加载的。

        代码(5)中的value被声明为volatile的,这是为了在多线程下保证内存可见性,value 是具体存放计数的变量。

        代码(2)(4)获取 value 变量在 AtomicLong 类中的偏移量。

下面重点看 AtomicLong 中的主要函数。

1. 递增和递减操作代码

调用unsafe方法,原子性设置value值为原始值+1,返回值为递增后的值
public final long incrementAndGet() {
    return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}
  //调用unsafe方法,原子性设置value值为原始值-1,返回值为递减之后的值  
  public final long decrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
    }
    //调用unsafe方法,原子性设置value值为原始值+1,返回值为原始值
    public final long getAndIncrement() {
        return unsafe.getAndAddLong(this, valueOffset, 1L);
    }
        //调用unsafe方法,原子性设置value值为原始值-1,返回值为原始值
    public final long getAndDecrement() {
        return unsafe.getAndAddLong(this, valueOffset, -1L);
    }

        在如上代码内部都是通过调用Unsafe的getAndAddLong方法来实现操作,这个函数是个原子性操作,这里第一个参数是 AtomicLong实例的引用,第二个参数是 value 变量在 AtomicLong 中的偏移值,第三个参数是要设置的第二个变量的值。

    public final long getAndAddLong(Object var1, long var2, long var4) {
        long var6;
        do {
            var6 = this.getLongVolatile(var1, var2);
        } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));

        return var6;
    }

boolean compareAndSet(long expect, long update )方法

    public final boolean compareAndSet(long expect, long update) {
        return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
    }

        由如上代码可知,在内部还是调用了unsafe.compareAndSwapLong方法。如果原子变量中的 value 值等于 expect,则使用 update 值更新该值并返回 true,否则返回 false。

        下面通过一个多线程使用 AtomicLong统计0的个数的例子来加深对AtomicLong 的理解。

/*
 * 统计0的个数
 * */
public class Atomic {


    //(10)创建Long型原子计数器
    private static AtomicLong atomicLong = new AtomicLong();

    //(11)创建数据源
    private static Integer[] arrayOne = new Integer[]{0,1,2,3,0,5,6,0,56,0};
    private static Integer[] arrayTwo = new Integer[]{10,1,2,3,0,5,6,0,56,0};
    public static void main(String[] args) throws InterruptedException {
        //(12)创建one统计数组arrayOne中0的个数
        Thread threadOne = new Thread(new Runnable() {
            @Override
            public void run() {
                int size = arrayOne.length;
                for(int i = 0; i < size; i++) {
                    if(arrayOne[i].intValue()==0){
                        atomicLong.incrementAndGet();
                    }
                }
            }
        });
        //(13)创建two统计数组arrayTwo中0的个数
        Thread threadTwo = new Thread(new Runnable() {
            @Override
            public void run() {
                int size = arrayTwo.length;
                for(int i = 0; i < size; i++) {
                    if(arrayTwo[i].intValue()==0){
                        atomicLong.incrementAndGet();
                    }
                }
            }
        });

        //(14)启动子线程
        threadOne.start();
        threadTwo.start();

        //(15)等待线程执行完毕
        threadOne.join();
        threadTwo.join();

        System.out.println("count 0 的数量为:"+ atomicLong.get());

    }
}

如上代码中的两个线程各自统计自己所持数据中0的个数,每当找到一个0就会调用AtomicLong的原子性递增方法。

        在没有原子类的情况下,实现计数器需要使用一定的同步措施,比如使用synchronized 关键字等,但是这些都是阻塞算法,对性能有一定损耗,而本文介绍的这些原子操作类都使用CAS非阻塞算法,性能更好。但是在高并发情况下AtomicLong还会存在性能问题。JDK8提供了一个在高并发下性能更好的LongAdder 类,下一篇我们来讲解这个类。

JAVA并发编程高级——JDK 新增的原子操作类 LongAdder-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值