在处理器中会使用总线锁或者缓存锁保证原子性,在java中可以通过锁和循环CAS的方式来实现原子操作
1.使用CAS实现原子操作
JVM中的CAS操作使用了处理器提供的CMPXCHG指令(交换指令)实现的。自旋CAS实现的基本思路就是 循环进行CAS操作直到成功。
2.CAS实现原子操作的三大问题
1)ABA问题
如果一个值原来是A,变成了B,又变回了A,那么使用CAS进行检查时会发现它的值没有发生改变,而实际上却变化了。解决该问题的思路就是增加一个版本号,改变一次版本号加1。从javva1.5开始,JDK的Atomic包提供了类AtomicStampedReference来解决ABA问题,该类中的compareAndSet方法就是首先检查当前引用是否等于预期引用,然后检查当前标志是否等于预期标志,若相等,则将该引用和该标志的值设置为给定的更新值。
2)循环时间长开销大
自旋CAS若长时间不成功,会给CPU带来非常大的执行开销。若JVM支持处理器提供pause指令,效率会有一定提升。pause指令的作用:(1)延迟流水线执行命令,使CPU不会消耗过多执行资源。(2)避免退出循环时因内存冲突而引起的CPU流水线清空。
3)只能保证一个共享变量的原子操作
解决方法:(1)使用锁。(2)把多个共享变量合并成一个共享变量来操作,比如i=2,j=a,合并为ij=2a。(3)从java1.5开始,JDK提供了AtomicReference类,可以将多个变量放在一个对象中来实现原子操作。
3.使用锁机制实现原子操作
JVM内部的锁有偏向锁,轻量级锁,互斥锁。但除了偏向锁,JVM实现锁的方式都运用了循环CAS,线程进入和退出同步块时都使用CAS来获取锁和释放锁。