不安全的累加代码,如下
1 public class Test { 2 long count = 0; 3 void add10K() { 4 int idx = 0; 5 while(idx++ < 10000) { 6 count += 1; 7 } 8 } 9 }
不安全的原因是count的可见性以及count += 1的原子性
使用AtomicLong
1 public class Test { 2 AtomicLong count = 3 new AtomicLong(0); 4 void add10K() { 5 int idx = 0; 6 while(idx++ < 10000) { 7 count.getAndIncrement(); 8 } 9 } 10 }
好处:
性能提升
原理:
硬件支持。CPU 为了解决并发问题,提供了 CAS 指令(CAS,全称是 Compare And Swap,即“比较并交换”)。
CAS 指令包含 3 个参数:共享变量的内存地址 A、用于比较的值 B 和共享变量的新值 C;并且只有当内存中地址 A 处的值等于 B 时,才能将内存中地址 A 处的值更新为新值 C。作为一条 CPU 指令,CAS 指令本身是能够保证原子性的。
源码:
1 final long getAndIncrement() { 2 return unsafe.getAndAddLong( 3 this, valueOffset, 1L); 4 }
思考:这里为什么要传this?来看一下AtomicLong这个类的结构
重点看成员变量,getAndAddLong传this和offset肯定是为了获取这个value值,但是为什么不直接传value?实际上这就是引用传值和非引用传值,value是成员变量,从共享内存读,如果直接非引用传值,那在value传入后加入内存屏障之前共享内存中value的值被修改了怎么办?
源码:
1 public final long getAndAddLong( 2 Object o, long offset, long delta){ 3 long v; 4 do { 5 // 读取内存中的值 6 v = getLongVolatile(o, offset); 7 } while (!compareAndSwapLong( 8 o, offset, v, v + delta)); 9 return v; 10 } 11 //原子性地将变量更新为x 12 //条件是内存中的值等于expected 13 //更新成功则返回true 14 native boolean compareAndSwapLong( 15 Object o, long offset, 16 long expected, 17 long x);
总结:
原子类针对单个共享变量,多个共享变量使用互斥锁
以上,还没说完,
另外还有CAS的ABA问题
等等搞一下openjdk的源码编译,看看compareAndSwapLong的实现
太特么打击人了,想编译成功太耗费时间,先抛了。