CAS是比较并交换,AtomicInteger
最终都是调用Unsafe.compareAndSwapInt
方法进行实现,那Unsafe.compareAndSwapInt
为什么是原子性的呢?它是怎么实现的?它的同步也是依赖于互斥吗?他与synchronized
锁的底层实现有什么不同吗? 这两种同步方式的场景选择?
Unsafe.compareAndSwapInt
为什么是原子性的?他的原子性是由硬件指令实现的,底层硬件通过将 CAS 里的多个操作在硬件层面语义实现上,通过一条处理器指令保证了原子性操作。这些指令如下所示: (1)测试并设置(Tetst-and-Set) (2)获取并增加(Fetch-and-Increment) (3)交换(Swap) (4)比较并交换(Compare-and-Swap) (5)加载链接/条件存储(Load-Linked/Store-Conditional)
前面三条大部分处理器已经实现,后面的两条是现代处理器当中新增加的。而且根据不同的体系结构,指令存在着明显差异。
在IA64,x86 指令集中有cmpxchg
指令完成 CAS 功能,在 sparc-TSO 也有 casa 指令实现,而在 ARM 和 PowerPC 架构下,则需要使用一对 ldrex/strex 指令来完成 LL/SC 的功能。在精简指令集的体系架构中,则通常是靠一对儿指令,如:load and reserve 和 store conditional 实现的,在大多数处理器上 CAS 都是个非常轻量级的操作,这也是其优势所在。
它采用了缓存锁定 现在都是多核 CPU 处理器,每个 CPU 处理器内维护了一块字节的内存,每个内核内部维护着一块字节的缓存,当多线程并发读写时,就会出现缓存数据不一致的情况。 此时,处理器提供:
synchronized
锁的底层实现有什么不同吗?它与synchronized
最大的不同就是,CAS
采用的缓存锁定,在没有竞争的时候没有额外的操作,当有竞争了才会有通知缓存失效机制。而synchronized
是采用悲观互斥锁
,即使没有线程竞争也会加上monitorenter
和monitorexit
指令(不考虑jdk1.6之后的锁优化),会有线程的阻塞行为,影响性能。
CAS - 竞争小的情况,竞争过多造成自旋过多,造成cpu空跑 synchronized - 竞争大的情况,竞争过小加悲观锁比较重