jdk里的CAS操作实现
在看轻量级锁加锁源码时,顺便看了下 jdk 里的 CAS 操作实现,本文记录下相关代码。
轻量级锁加锁的时候,如果处于无锁状态,则会通过 CAS 操作将锁对象的 Mark Word 更新为指向 Lock Record 的指针,相关代码如下:
1 | if (mark->is_neutral()) { |
其中 CAS 操作是通过 Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)
这段代码实现的。它有 3 个参数:
- 第一个参数是 exchange_value(新值)
- 第二个参数是 dest(目标地址)
- 第三个参数是 compare_value(原值)
它的含义是:如果目标地址的值是原值,则将其更新为新值,并且返回原值。否则,不做更新,并且返回新值。因此,当返回结果是原值时,则说明更新成功
Atomic::cmpxchg_ptr
的实现与硬件相关,因此对应会有多个实现
以 linux x86 为例,它的 int 类型的 CAS 实现如下:
1 | inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value) { |
它是调用的 cmpxchg 方法,相关实现如下:
1 | inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compare_value) { |
先看第一行 int mp = os::is_MP()
,它的实现如下:
1 | // Interface for detecting multiprocessor system |
这是判断是否为多处理器,如果是多处理器则返回 true。判断结果会最终生成指令的参数
再看下 LOCK_IF_MP(%4)
的实现:
1 | // Adding a lock prefix to an instruction on MP machine |
它的作用是针对多核处理器指令添加 lock
前缀。最后看下如下关键代码:
1 | __asm__ volatile (LOCK_IF_MP(%4) "cmpxchgl %1,(%3)" |
这里 asm 表示后面的是汇编指令,volatile 表示编译器不需要优化代码,括号中间则是汇编代码。简单来看,单核处理器使用 cmpxchgl
命令实现 CAS 操作,多核处理器使用带 lock
前缀的 cmpxchgl
命令实现 CAS 操作。
相对来说,windows_x86 的实现可能看起来更直观些
1 | // Adding a lock prefix to an instruction on MP machine |
同样也需要判断是否为多核处理器,只是寄存器操作更直观
- 2019-03-13
并发编程中经常会使用到 volatile 关键字,使用该关键字修饰的共享变量可以保证多线程之间的可见性,也就是说当一个线程修改了变量的值,另一个线程能够读到修改后的值。