CAS自旋锁

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

昨天写了篇关于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包中的原子类就是基于自旋锁实现的!!!!!!!!!!



### 3.1 CAS自旋锁的关系 CAS(Compare-And-Swap)是一种底层的原子操作机制,通常由 CPU 指令直接支持,用于实现多线程环境下的无锁同步。它通过比较并交换两个值的方式,确保在并发操作中数据的一致性[^1]。CAS 本身并不是一种锁,而是一种构建锁机制的基础工具。 自旋锁是一种基于 CAS 实现的同步机制。其核心思想是,当线程尝试获取锁失败时,不会立即进入阻塞状态,而是通过循环不断尝试获取锁,直到成功为止。这种方式减少了线程上下文切换的开销,但会增加 CPU 的使用率[^2]。 ### 3.2 CAS 是否属于自旋锁机制 CAS 本身不属于自旋锁,但它是实现自旋锁的基础。自旋锁的实现通常依赖于 CAS 操作来判断和更新锁的状态。例如,在一个简单的自旋锁实现中,线程会通过 CAS 操作尝试将锁的状态从“未锁定”(如 0)更改为“已锁定”(如 1)。如果操作失败,线程会继续循环尝试,直到成功获取锁为止。以下是基于 CAS自旋锁实现示例: ```java public class SpinLock { private AtomicInteger lock = new AtomicInteger(0); public void lock() { while (!lock.compareAndSet(0, 1)) { // 自旋等待 } } public void unlock() { lock.set(0); } } ``` 上述代码中,`compareAndSet`方法是基于 CAS 实现的原子操作,用于确保锁的获取和释放具有原子性[^3]。 ### 3.3 CAS自旋锁的优缺点 CAS自旋锁都属于乐观并发控制的范畴,适用于低竞争场景[^1]。它们的优点包括减少线程阻塞和上下文切换的开销,从而提高性能。然而,它们也存在一些缺点: - **CAS 的缺点**:在高竞争场景下,CAS 操作可能会频繁失败,导致性能下降。此外,CAS 无法解决“ABA 问题”,即一个值被修改后又恢复为原始值,可能导致错误的判断。 - **自旋锁的缺点**:由于线程在等待锁时会持续循环,因此会消耗较多的 CPU 资源,尤其在锁竞争激烈或持有锁时间较长的情况下。 ###
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值