Loading... CAS是Compare And Swap。它是java.util.concurrent包的基石。 我们知道,如果线程直接对volatile变量赋值(原子性操作),也是可以实现线程安全的,但更多情况,我们线程对volatile变量的修改与volatile变量的当前值是有关系的!!,这就需要先对这个volatile变量读,然后再写,这就有了至少两个步骤了,所以这个操作不是原子性的,所以就不能保证线程安全。 由此,java使用CAS操作,通过底层实现将变量读-变量写过程变为原子执行!其利用到了缓存锁定,通过将其他各个线程中的该变量的缓存锁定,不允许操作,然后在本线程对该变量进行修改,然后再刷入主内存。 下面以AtomicInteger来研究CAS的具体实现过程。 ```java /** * AtomicInteger.java */ private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static{ try{ .....初始化valueOffset..... } } private volatile int value; public final int get(){ return value; } public final int incrementAndGet(){ return unsafe.getAndAddInt(this, valueOffset, 1); } ``` ```java /** * Unsafe.java * getIntVolatile 方法和 compareAndSwapInt 为本地方法调用 * o 为需要修改的对象 * offset为需要修改字段到对象头的偏移量 * delta为更新增加量 * expected为期望值 * x为更新值 */ public final int getAndInt(Object o, long offset, int delta){ int v; do { v = getIntVolatile(o, offset); }while (!compareAndSwapInt(o, offset, v, v + delta)); return v; } public native int getIntVolatile(Object o, long offset); // 该方法即为CAS,它借助C语言调用CPU底层指令实现,大致流程是 判断对象o中要修改的字段的值是否为 expected。当相同时,将x值写入o public native boolean compareAndSwapInt(Object o, long offset, int expected, int x); ``` ```c /** * compareAndSwapInt本地方法的源代码片段 */ // mp为处理器核心数量 int mp = os::is_MP(); __asm { mov edx, dest mov ecx, exchange_value mov eax, compare_value // 带有此LOCK前缀的指令在执行期间可以保证执行的原子性(通过锁缓存)、禁止指令重排序、将写缓冲区中所有数据刷新到内存 LOCK_IF_MP(mp) // cmpxchg 指令 将AL/AX/EAX/RAX寄存器中的值与首操作数比较,如果相等,则将第二操作数的值装载到首操作数 由于edx装的是指针,所以这里首操作数为 dword ptr [edx],表明这该指针指向的单元是一个double word (双字单元), 即4字节。 cmpxchg dword ptr [edx], ecx } ``` LOCK前缀有如下说明: * 确保对内存的读写操作原子执行(在这里使cmpxchg原子执行) * 禁止该指令(cmpxchg)与之前和之后的读和写指令重排序 (即 使其具有volatile读、写语义) * 把写缓冲区的所有数据刷到内存中 (上一条的实现,必须具备的能力) 综上所述,可以得出结论: * CAS操作的底层实现用到了LOCK前缀和cmpxchg指令,它将CAS成为一个原子操作 最后修改:2021 年 04 月 22 日 06 : 51 PM © 允许规范转载