JUC并发编程——CAS 介绍及底层源码分析

本文深入探讨了Java并发编程中的CAS(Compare And Swap)机制,解释了CAS如何避免锁带来的开销,以及其在乐观锁中的作用。通过JDK的Unsafe类了解了CAS操作的实现,并通过AtomicInteger的源码分析展示了CAS的原子性。此外,文章还讨论了CAS的ABA问题及其解决方案——AtomicStampedReference。

JUC并发编程——CAS 介绍及底层源码分析

1、什么是 CAS

前言:使用锁的代价

Java并发处理中锁非常重要,但是使用锁会带来下面几个问题:

  • 加锁、释放锁会需要操作系统进行上下文切换和调度延时,在上下文切换的时候,cpu之前缓存的指令和数据都将失效,这个过程将增加系统开销。
  • 多个线程同时竞争锁,锁竞争机制本身需要消耗系统资源。没有获取到锁的线程会被挂起直至获取锁,在线程被挂起和恢复执行的过程中也存在很大开销。
  • 等待锁的线程会阻塞,影响实际的使用体验。如果被阻塞的线程优先级高,而持有锁的线程优先级低,将会导致优先级反转(Priority Inversion)。

乐观锁和悲观锁

乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。

悲观锁:总是假设最坏的情况,认为线程每次访问共享资源的时候,总会发生冲突,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。宁愿牺牲性能(时间)来保证数据安全。synchronized 关键字的实现就是悲观锁。

乐观锁:顾名思义,就是很乐观,每次线程访问共享资源不会发生冲突,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,一旦检测到冲突产生,就重试当前操作直到没有冲突为止。CAS机制(Compare And Swap)就是一种乐观锁。

CAS (Compare And Swap) 比较并交换

CAS (Compare And Swap) 比较并交换是一种现代 CPU 广泛支持的CPU指令级的操作,只有一步原子操作,所以非常快。而且CAS避免了请求操作系统来裁定锁的问题,不用麻烦操作系统,直接在CPU内部就搞定了。

CAS的功能是判断内存某个位置的值是否为预期值。如果是则更改为新的值,这个过程是原子的。CAS可以将read- modify - write这类的操作转换为原子操作。

read- modify - write类型的操作,如i++,i++自增操作包括三个子操作:

● 从主内存读取i变量值

● 对i的值加1

● 再把加1之后 的值保存到主内存

CAS原理:在把数据更新到主内存时,再次读取主内存变量的值,如果现在变量的值与期望的值(操作起始时读取的值)一样就更新。

CAS 有三个操作参数:

  1. 内存位置 V(它的值是我们想要去更新的)
  2. 预期原值 A(上一次从内存中读取的值)
  3. 新值 B(应该写入的新值)

CAS的操作过程:将内存位置V的值与A比较(compare),如果相等,则说明没有其它线程来修改过这个值,所以把内存V的的值更新成B(swap),如果不相等,说明V上的值被修改过了,不更新,而是返回当前V的值,再重新执行一次任务再继续这个过程。

2、JDK 对 CAS 的支持

JDK 提供了对CAS 操作的支持,具体在sun.misc.unsafe类中,声明如下:

public final native boolean compareAndSwapObject(Object o, long offset,
                                                 Object expected,
                                                 Object x);
public final native boolean compareAndSwapInt(Object o, long offset,
                                                  int expected,
                                                  int x);                                               
public final native boolean compareAndSwapLong(Object o, long offset,
                                                   long expected,
                                                   long x);  

参数说明:

  • o:表示要操作的字段对象
  • offset:字段在对象内的偏移量,通过这个偏移量迅速定位字段并设置或获取该字段的值
  • expected:字段的期望值
  • x:如果该字段的值等于字段的期望值,用于更新字段的新值

Unsafe是CAS的核心类,由于Java方法无法直接访问底层系统,需要通过本地(Native)方法来访问,Unsafe相当于一个后门,基于该类可以直接操作特定的内存数据,Unsafe类存在sun.misc包中,其内部方法操作可以像C指针一样直接操作内存,Java中的CAS操作的执行依赖于Unsafe类的方法。

Unsafe 类中操作内存数据的方法:

//分配内存指定大小的内存
public native long allocateMemory(long bytes);

//根据给定的内存地址address设置重新分配指定大小的内存
public native long reallocateMemory(long address, long bytes);

//用于释放allocateMemory和reallocateMemory申请的内存
public native void freeMemory(long address);

//将指定对象的给定offset偏移量内存块中的所有字节设置为固定值
public native void setMemory(Object o, long offset, long bytes, byte value);

//设置给定内存地址的值
public native void putAddress(long address, long x);

//获取指定内存地址的值
public native long getAddress(long address);

//设置给定内存地址的long值
public native void putLong(long address, long x);

//获取指定内存地址的long值
public native long getLong(long address);


//其他基本数据类型(long,char,float,double,short等)的操作与putByte及getByte相同
public native byte getByte(long address);

public native void putByte(long address, byte x);

//操作系统的内存页大小
public native int pageSize();

注意Unsafe类的所有方法都是native修饰的,也就是说unsafe类中的方法都直接调用操作系统底层资源执行相应的任务。

Java中CAS的操作:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

万里顾—程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值