由于Java的原子操作最多只支持32位的,而long
和double
是64位的,因此它们不具有原子性。
而在java中定义了AtomicLong
这个类,来实现原子的long
类型。那具体是怎么实现的?我们先来了解下CAS。
Compare And Swap(CAS)
CAS的原理是拿期望的值和原本的一个值作比较,如果相同则更新为新的值,此处“原本的一个值”怎么来的,我们先看看AtomicLong
类的几个实现:
1 | //value值的偏移量 |
这里用到UnSafe
类的一个方法objectFieldOffset()
:
1 | /** |
这个方法是用来拿到我们上文提到的“原本的一个值”的内存地址。是一个本地方法,返回值是valueOffset
。它的参数field
就是AtomicLong
里定义的value
属性:
1 | private volatile long value; |
value
是由volatile
修饰的变量,在内存中可见,因此可以保证任何时刻任何线程都能拿到该变量的最新值。此处value
的值,可以在AtomicLong
类初始化的时候传入,也可以留空,留空则自动赋值为0
。
然后我们再看下incrementAndGet()
这个方法是如何实现CAS的:
1 | /** |
这个方法又调用了unsafe
的getAndAddLong()
:
1 | public final long getAndAddLong(Object o, long offset, long delta) { |
再继续看compareAndSwapLong()
方法:
1 | /** |
我们知道,如果我们创建AtomicLong
实例时不传入参数,则原始变量的值即为0。所以上面v = this.getLongVolatile(o, offset);
拿到的值为0
。而this.compareAndSwapLong()
这个方法是Java本地的方法,由于Java语言无法访问到操作系统底层(如硬件等),所以Java使用native
来扩展功能。这个CAS实现具体由C或C++来完成的。
大概的实现原理是:有3个操作数,内存值M,预期值E,新值U,如果M==E,则将内存值修改为U,返回true,否则返回false。如果为false,会继续拿期望的值与offset
原始值做比较,知道成功为止。
这篇文章简单的模拟一个CAS实现,当然这不是真正的CAS实现,真正的CAS是CPU
指令来实现的。