为了解决常见的基本数据类型在并发编程的时候不受线程安全影响问题,提出了原子类的方案
原子类在解决了线程安全的同时,解决了互斥锁方案的加锁解锁的繁琐操作
无锁方案的实现
解决原子类的方案源于了硬件提供了CAS指令,(Compare And Swap)这个CPU指令提供了三个参数,共享变量的地址
比较的值B和新值C,只有A的值等于B,才能把A换为C,作为一条CPU指令,可以保证原子性
通过Java的代码实现可以简化为
class SimulatedCAS{
int count;
synchronized int cas(
int expect, int newValue){
// 读目前 count 的值
int curValue = count;
// 比较目前 count 值是否 == 期望值
if(curValue == expect){
// 如果是,则更新 count 的值
count = newValue;
}
// 返回写入前的值
return curValue;
}
}
Cas对于Count+=1来说,在A+1写入的时候,会拿着A来比较内存中的值,如果相等,才将A换为A+1,这就是CAS的语义
通常关于CAS指令来说,都会伴随着自旋,通常,实现一个线程安全的Count += 1 就利用了 while()指令,如果cas(count,newValue)返回的值不是count,就说明已经被更新过了,一直进行while,直到更新成功
由于是利用了CPU指令,所以这使用原子类性能会好很多
在Java中,使用CAS直接使用原子化 count =+ 1的内部流程为
//内部的流程如下,
//会循环调用 compareAndSwapLong(),并且会返回true和false
public final long getAndAddLong(
Object o, long offset, long delta){
long v;
do {
// 读取内存中的值
v = getLongVolatile(o, offset);
} while (!compareAndSwapLong(
o, offset, v, v + delta));
return v;
}
//原子性地将变量更新为x
//条件是内存中的值等于expected
//更新成功则返回true
native boolean compareAndSwapLong(
Object o, long offset,
long expected,
long x);
同样原子类中常见的CompareAndSet()也是返回的true或者false
原子类概述
JDK提供的原子类分为了
原子化的基本数据类型
原子化的对象引用类型
原子化数组
原子化对象属性更新器
原子化累加器
基本属性中的原子类有
AtomicBoolean、AtomicInteger 和 AtomicLong
提供的方法有: getAndIncrement() //原子化i++ getAndDecrement() //原子化的i– incrementAndGet() //原子化的++i decrementAndGet() //原子化的–i //当前值+=delta,返回+=前的值 getAndAdd(delta) //当前值+=delta,返回+=后的值 addAndGet(delta) //CAS操作,返回是否成功 compareAndSet(expect, update) //以下四个方法 //新值可以通过传入func函数来计算 getAndUpdate(func) updateAndGet(func) getAndAccumulate(x,func) accumulateAndGet(x,func) |
引用类型
AtomicReference、AtomicStampedReference 和 AtomicMarkableReference
对象的引用类型需要我们关注ABA问题 ,AtomicStampedReference 和 AtomicMarkableReference通过增加了版本号来解决了这个问题 AtomicStampedReference 通过一个版本号参数来实现的 AtomicMarkableReference 通过Boolean值来简化了版本号机制 |
原子化数组
AtomicIntegerArray、AtomicLongArray 和 AtomicReferenceArray
对应着 原子Int数组,原子Long数组,原子引用数组 只不过是增加了一个数组的索引参数 |
原子化的对象属性更新
AtomicIntegerFieldUpdater、AtomicLongFieldUpdater 和 AtomicReferenceFieldUpdater
原子化的更新对象的属性,都是通过反射方式的更新 但是要求对象的属性必须是volatile类型的,为了保证可见性 boolean compareAndSet(T obj, int expect, int update) |
原子的累加器
DoubleAccumulator、DoubleAdder、LongAccumulator 和 LongAdder |