原子性
处理器保障从系统内存中读取和写入字节是原子的,即其他处理器不能访问该字节地址。
总线锁保障原子性
处理器发出Lock#信号到总线上,其他处理器阻塞,该处理器独享内存。
缓存锁保障原子性
总线锁把CPU和内存通信锁住,其他CPU无法访问其他内存地址,开销较大。
可以在缓存中,锁定内存地址,保障原子性。
缓存锁定缓存行时,回写内存不声明LOCK#,而是修改内部内存地址,同时通过缓存一致性阻止其他处理器修改;当其他处理器回写
,将会无效缓存行,从内存重新加载数据。
JAVA实现原子操作
通过锁实现原子操作
锁机制保障只有获取锁的线程才能操作内存区域,常见的JVM锁有偏向锁、轻量锁和互斥锁。
除偏向锁外,其他锁实现都采用CAS进行锁的获取和释放。
通过CAS实现原子操作
CAS通过Compare And Swap可以实现原子操作;原子操作即不能被进一步分割的最小粒子。
CAS需要输入两个参数,old值和expect值;若old值未发生变化,则替换old为expect,否则不交换。实际上是三个参数,第三个参数无需输入,可使用局部变量或者由操作指令维护,是old的origin值,用于和输入的old比较。
JVM中的CAS操作正是利用了处理器的CMPXCHG指令实现,自旋CAS实现的就是循环CAS直到成功。
从Java 1.5开始,JDK的JUC包里提供了原子操作,如AtomicBoolean(用原子
方式更新的boolean值)、AtomicInteger(用原子方式更新的int值)和AtomicLong(用原子方式更
新的long值)。这些原子包装类还提供了以原子的方式将当前值自增1和自减1。
CAS产生的问题
ABA问题
假如old和expect值相同,虽然交换了,但结果貌似没有变化,而且也不知道是否真的交换成功。
因此需要引入版本号,假如每次交换后版本加1,则可以明显确定是否交换成功,例如AtomicStampedReference。
长时间自旋
如果长时间CAS自旋失败,则浪费CPU。
可以提供暂停操作,实现自旋延迟效果。
只能支持单个变量的原子性
多个变量原子性操作通常需要用锁,或者采用把多个共享变量合并成一个共享变量。
AtomicReference支持对象的原子性,可以将多个变量放在一个对象中进行CAS。