处理器提供总线锁定和缓存锁定两个机制来保证复杂内存操作的原子性。
使用总线锁保证原子性
想要保证读改写共享变量的操作是原子的,就必须保证CPU1读改写共享变量的时候,CPU2不能操作缓存了该共享变量内存地址的缓存。
总线锁就是使用处理器提供的一个LOCK#信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被则阻塞住,那么该处理器就可以独占共享内存。
使用缓存锁保证原子性
因为总线锁会把CPU和内存之间的通信锁住,这使得锁定期间,其他处理器不能操作其他内存地址的数据,所以总线锁定的开销比较大,目前处理器在某些场合下使用缓存锁定代替总线锁定来优化。
缓存锁定是指内存区域如果被缓存在处理器的缓存行中,并且在Lock操作期间被锁定,那么当它执行锁操作回写到内存时,处理器不在总线上声言Lock#信号,而是修改内部的内存地址,并允许它的缓存一致性机制来保证操作的原子性,因为缓存一致性机制会阻止同时修改两个以上处理器缓存的内存区域数据,当其他处理器回写已被锁定的缓存行的数据时,会使缓存行无效。
有两种情况不会使用缓存锁定:
- 当操作的数据不能被缓存在处理器内部,或操作的数据跨多个缓存行时,则处理器调用总线锁定
- 有些处理器不支持缓存锁定
Java通过锁和循环CAS的方式来实现原子操作
CAS实现原子操作的三大问题:
- ABA问题
- 循环时间长开销大
- 只能保证一个共享变量的原子操作