引言
我们上面介绍的原子类,都是借助了硬件提供的LOCK CMPXCHG指令,并且在外层加了循环,不断去进行更新操作直到更新成功。这样的操作会有几个问题,我们来看一下。
ABA问题
这个我们应该很熟悉了,因为LOCK CMPXCHG指令在执行时,首先会检查内存位置的当前值是否等于寄存器中的当前值,如果相等,才会进行替换操作。如果内存位置的值原来是A,被一个线程设置为B,后被一个线程设置为A,再次执行的时候,会发现它没有发生变化,实际上是变化了的。
ABA的解决思路是使用版本号。每次变量更新的时候把版本号加1,那么A->B->A就会变成1A->2B->3A。Atomic包下面的AtomicStampedReference就是用来解决这个问题的。
忙循环的开销问题
因为CAS会不断地进行循环直到更新成功,如果长时间不能更新成功,那么线程会一直占用cpu,造成cpu资源的浪费。
只能保证一个共享变量的原子操作
说到底,循环CAS底层采用的是LOCK CMPXCHG命令,该命令只有一个内存位置的操作数,所以循环CAS只能对一个共享变量执行操作,如果对相对多个共享变量进行操作,CAS就不能保证原子性了。也可以把多个共享变量合并成一个共享变量来操作,JDK Atomic包下面的AtomicReference就是这样来解决问题的。