原子性提供了互斥访问,同一时刻只会有一个线程对资源进行操作。
该包下提供了具有原子性的数据类型。它是通过CAS来实现原子性的。
java.util.concurrent.atomic
以AtomicInteger的incrementAndGet()方法为例
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
它使用了Unsafe类的getAndAddInt方法,追踪到该方法查看源码
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;
}
里面的compareAndSwap方法简称就是CAS
native表示不是由Java语言实现的,是由其他语言实现的
主要作用
1.直接操作内存
//分配内存
public native long allocateMemory(long var1);
//重新分配内存
public native long reallocateMemory(long var1, long var3);
public native void setMemory(Object var1, long var2, long var4, byte var6);
public void setMemory(long var1, long var3, byte var5) {
this.setMemory((Object)null, var1, var3, var5);
}
public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);
public void copyMemory(long var1, long var3, long var5) {
this.copyMemory((Object)null, var1, (Object)null, var3, var5);
}
//释放内存
public native void freeMemory(long var1);
2.字段的定位与修改
3.线程的挂起与恢复
LockSupport调用的最底层就是UnSafe里面的unpark park
public native void unpark(Object var1);
public native void park(boolean var1, long var2);
4.CAS操作(乐观锁)
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
CAS指令的三个操作数,当前内存地址存放的实际值,预期值,更新的新值。
当且仅当预期值和当前内存地址存放的实际值相同时,将内存值修改为新值,否则重试,直到成功为止
上面AtomicInteger(原子更新基本类型都与此雷同、原子更新数组类型比如AtomicIntegerArray也基本相同,基本一致,只不过在AtomicIntegerArray的方法中会多一个指定数组索引位i)追踪的代码用伪代码表示为:
boolean flag = false;
do{
获取当前内存地址中实际值
if (实际值==预期值){
往内存地址中写入新值
flag = true (结束循环)
}
}while(!flag)
优点:
1.非阻塞算法
2.原子操作成本低
缺点:
1.ABA问题。一个旧值A变为了成B,然后再变成A,在做CAS时检查发现旧值为A,但是实际上却是发生了变化。解决方案可以用乐观锁方式,添加一个版本号。原来的变化路径A->B->A就变成了1A->2B->3C。
例如:AtomicStampedReference原子更新引用类型,这种更新方式会带有版本号,解决CAS的ABA问题。
2.自旋时间太长。CAS如果长时间不成功,会给CPU带来非常大的执行开销。
3.只能保证一个共享变量的原子操作。JDK提供了AtomicReference类来保证引用对象之间的原子性,可以把多个变量放在一个对象里来进行CAS操作。