CAS自旋锁

自旋锁是一种在多处理器系统中用于互斥访问资源的机制,它让未获取锁的进程循环检查锁是否已释放。与互斥锁不同,自旋锁不阻塞线程,但可能导致CPU资源过度占用。Java中的CAS(Compare And Swap)是一种无锁原语,用于在预期值和内存值相等时原子性地更新内存值。Unsafe类提供了CAS操作,但使用时需谨慎,因为它涉及底层内存操作,误用可能导致严重问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

昨天写了篇关于AtomicInteger的博客,感觉觉还是不太完整,所以又把自旋锁的知识整理了一下。。。。。。。。。

什么是自旋锁

       说道自旋锁就要从多线程下的锁机制说起,由于在多处理器系统环境中有些资源因为其有限性,有时需要互斥访问(mutual exclusion),这时会引入锁的机制,只有获取了锁的进程才能获取资源访问。即每次只能有且只有一个进程能获取锁,才能进入自己的临界区,同一时间不能两个或两个以上进程进入临界区,当退出临界区时释放锁。

     设计互斥算法时总是会面临一种情况,即没有获得锁的进程怎么办?

     通常有2种处理方式:

           一种是没有获得锁的调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,这就是本文的重点——自旋锁。他不用将线城阻塞起来(NON-BLOCKING)。

          另一种是没有获得锁的进程就阻塞(BLOCKING)自己,继续执行线程上的其他任务,这就是 ——互斥锁(包括内置锁Synchronized还有ReentrantLock等等)。

自旋锁是如何实现的呢???

     跟互斥锁一样,一个执行单元要想访问被自旋锁保护的共享资源,必须先得到锁,在访问完共享资源后,必须释放锁。如果在获取自旋锁时,没有任何执行单元保持该锁,那么将立即得到锁;如果在获取自旋锁时锁已经有保持者,那么获取锁操作将自旋在那里,直到该自旋锁的保持者释放了锁。由此我们可以看出,自旋锁是一种比较低级的保护数据结构或代码片段的原始方式(有时我也感觉自旋锁严格意义上称不上是一种锁)。

    通过了解自旋锁的原理其实不难发现,这种锁是有缺陷的:

    归死锁:试图递归地获得自旋锁必然会引起死锁:递归程序的持有实例在第二个实例循环,以试图获得相同自旋锁时,不会释放此自旋锁。

     所以在递归程序中使用自旋锁应遵守下列策略:

           递归程序决不能在持有自旋锁时调用它自己,也决不能在递归调用时试图获得相同的自旋锁。

           此外如果一个进程已经将资源锁定,那么,即使其它申请这个资源的进程不停地疯狂“自旋”,也无法获得资源,从而进入死循环。

     过多占用cpu资源:如果不加限制,由于申请者一直在循环等待,因此自旋锁在锁定的时候,如果不成功,不会睡眠,会持续的尝试,单cpu的时候自旋锁会让其它process动不了. 因此,一般自旋锁实现会有一个参数限定最多持续尝试次数. 超出后, 自旋锁放弃当前time slice. 等下一次机会。

综上所述:自旋锁比较适用于锁使用者保持锁时间比较短的情况。正是由于自旋锁使用者一般保持锁时间非常短,因此选择自旋而不是睡眠是非常必要的,自旋锁的效率远高于互斥锁。

Java中的CAS

      CAS是一种系统原语(所谓原语属于操作系统用语范畴。原语由若干条指令组成的,用于完成一定功能的一个过程。primitive or atomic action 是由若干个机器指令构成的完成某种特定功能的一段程序,具有不可分割性·即原语的执行必须是连续的,在执行过程中不允许被中断)。CAS是Compare And Set的缩写。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

      sun.misc.Unsafe是JDK里面的一个内部类(哈哈。。。有没有发现AtomicInteger源码中中就有一个Unsafe的成员变量啊),这个类为JDK严格保护,因为他提供了大量的低级的内存操作和系统功能。如果因为错误的使用了这个类,不会有“异常”被扔出,甚至会造成JVM宕机。这也是为什么这个类的名字是Unsafe的原因。因此当使用这个类的时候,你一定要明白你在干什么(不过一般谁会用它呀)。这个类中提供了3个CAS的操作:

方法名解释
compareAndSwapInt(Object object, long address, int expected, int newValue) 比较对象object的某个int型的属性(以地址的方式访问),如果他的数据值是expected,则设定为newValue,返回true;否则返回false
compareAndSwapLong(Object object, long address, long expected, long newValue) 比较对象object的某个long型的属性(以地址的方式访问),如果他的数据值是expected,则设定为newValue,返回true;否则返回false
compareAndSwapLong(Object object, long address, Object expected, Object newValue) 比较对象object的某个Object型的属性(以地址的方式访问),如果他的数据值是expected,则设定为newValue,返回true;否则返回false

看到没有,AtomicInteger中的compareAndSet()方法就是表中红色字体的方法实现的。下面看看AtomicInteger的源码就一目了然了:

import sun.misc.Unsafe;  
  
public class AtomicInteger extends Number implements java.io.Serializable {  
    private static final long serialVersionUID = 6214790243416807050L;  
  
    // setup to use Unsafe.compareAndSwapInt for updates  
    <span style="color:#ff0000;">private static final Unsafe unsafe = Unsafe.getUnsafe(); </span> 
    private static final long valueOffset;  
  
    static {  
      try {  
        valueOffset = unsafe.objectFieldOffset  
            (AtomicInteger.class.getDeclaredField("value"));  
      } catch (Exception ex) { throw new Error(ex); }  
    }  
  
    private volatile int value;  

   //如果当前值 ==预期值,则以原子方式将该值设置为给定的更新值。如果成功就返回,否则返回,并且不修改原值。  
public final boolean compareAndSet(int expect, int update) {  
    return <span style="color:#ff0000;">unsafe.compareAndSwapInt(this, valueOffset, expect, update);  </span>
    }  

    //以原子方式将当前值加1。相当于线程安全版本的i++操作。  
    public final int getAndIncrement() {  
        for (;;) {  
            int current = get();  
            int next = current + 1;  
            if (compareAndSet(current, next))  
                return current;  
        }  
    }  
  

}  

从源代码中可以看出,Atomic包中的原子类就是基于自旋锁实现的!!!!!!!!!!



<think>好的,我现在需要回答用户关于CAS锁和自旋锁的关系,以及它们的原理、区别和联系。首先,我得回顾一下自己对这些概念的理解,确保准确无误。 根据引用[5],CAS是Compare-and-Swap的缩写,属于原子操作,常用于并发编程中的乐观锁机制。比如Java中的Atomic类就是基于CAS实现的。而自旋锁,我之前学的是指线程在获取锁失败时不会立即阻塞,而是循环尝试获取,直到成功。这通常在高并发但锁持有时间短的场景下使用,避免线程切换的开销。 用户的问题是CAS锁是否等同于自旋锁,以及它们的区别和联系。首先,我需要明确这两个概念是否属于同一层面。CAS是一种具体的原子操作,而自旋锁是一种锁的实现策略。所以它们可能不是同一个概念,但可能有关联。比如,自旋锁的实现可能会使用CAS操作来保证原子性。 根据引用[2],乐观锁如CAS通过比较并交换来更新值,而自旋锁可能属于乐观锁的一种实现方式,因为它不断循环尝试获取锁,而不是挂起线程。不过,自旋锁也可能使用其他机制,比如忙等待,而CAS是实现其原子性的手段。 另外,引用[4]提到偏向锁和轻量级锁在竞争少时的优化,而自旋锁可能在轻量级锁的实现中发挥作用。但这里可能涉及不同的JVM内部机制,需要区分用户层的自旋锁和JVM内部的自旋优化。 现在需要整理两者的关系:CAS是实现机制,自旋锁是行为模式。自旋锁可以用CAS来实现,但CAS不仅仅用于自旋锁,还可以用于其他如无锁数据结构。反过来,自旋锁也可以不用CAS,比如使用其他原子操作,但通常CAS是实现自旋锁的常见方式。 区别方面,CAS是原子操作,关注的是如何安全地修改数据;自旋锁是锁的一种,关注的是线程如何等待锁。联系在于自旋锁的实现常依赖CAS来检测和获取锁的状态变化。 需要确保回答中涵盖这些点,并且引用正确的参考文献。例如,引用[2]和[5]说明CAS作为乐观锁的实现,引用[4]提到自旋锁在不同竞争情况下的表现,但可能不直接说明两者的关系,需要结合其他知识。 总结:CAS锁(通常指基于CAS的乐观锁)和自旋锁不同,但自旋锁的实现常使用CAS。它们的区别在于前者是原子操作,后者是锁策略;联系在于CAS可用于实现自旋锁。</think>### CAS锁与自旋锁的关系分析 #### 1. **基本概念** - **CAS锁**:本质是**基于CAS原子操作的乐观锁机制**,通过不断尝试比较并交换值来实现并发控制(如Java中的`AtomicInteger`)[^5]。 - **自旋锁**:是一种**锁的等待策略**,线程在获取锁失败时不会阻塞,而是循环(自旋)重试,直到成功获取锁[^4]。 #### 2. **原理对比** | **维度** | **CAS锁** | **自旋锁** | |----------------|--------------------------------------|------------------------------------| | **核心机制** | 依赖CPU的CAS指令实现原子操作 | 依赖循环尝试(可能结合CAS或原子变量) | | **应用场景** | 无锁数据结构、乐观锁实现[^2] | 短时锁竞争、避免线程切换开销 | | **资源消耗** | 循环CAS可能引发CPU空转(类似自旋) | 自旋本身会导致CPU占用 | #### 3. **联系与区别** - **联系**: 自旋锁的实现**通常依赖CAS操作**。例如,线程通过CAS原子性地修改锁状态(如从0变为1),若失败则循环重试。 ```java // 伪代码:自旋锁CAS实现 while (!CAS(lock, 0, 1)) { // 自旋直到成功 // 空循环或短暂休眠 } ``` - **区别**: 1. **抽象层级不同**:CAS是原子操作指令,属于底层实现;自旋锁是锁策略,属于高层设计。 2. **用途范围不同**:CAS可用于实现自旋锁、无锁队列等;自旋锁仅是锁的一种行为模式。 3. **失败处理不同**:CAS操作失败后可由调用方决定后续动作(如重试或放弃);自旋锁失败后强制循环重试。 #### 4. **实际应用中的结合** - **示例1**:Java的`ReentrantLock`在尝试获取锁时,**先通过CAS快速尝试**,失败后再进入阻塞队列[^1]。 - **示例2**:轻量级锁(JVM层面)在无竞争时使用CAS+自旋,竞争激烈时升级为重量级锁。 --- ### 总结 - **CAS锁 ≠ 自旋锁**,但**自旋锁常通过CAS实现**。 - CAS是工具,自旋是策略,二者在并发控制中常协同工作。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值