自旋锁是什么?

本文内容如有错误、不足之处,欢迎技术爱好者们一同探讨,在本文下面讨论区留言,感谢。

定义

自旋锁 spin lock

下面内容摘自维基百科

在这里插入图片描述

在软件工程中,自旋锁是一种锁,它使试图获取它的线程在循环(“自旋”)中简单地等待,同时反复检查该锁是否可用。由于线程保持运行状态,但未执行有用的任务,因此使用这种锁是一种繁忙的等待。一旦获得了自旋锁,通常将一直保持这种状态,直到显式释放它们为止,尽管在某些实现中,如果正在等待线程(持有锁的线程)阻塞或“进入睡眠状态”,则它们可能会自动释放。

因为自旋锁避免了操作系统进程上下文切换带来的CPU消耗,所以如果线程可能仅在短时间内被阻塞,则自旋锁非常有效。因此,操作系统内核经常使用自旋锁。但是,自旋锁如果保持更长的时间会变得很浪费,因为它们可能会阻止其他线程运行。线程持有锁的时间越长,则持有该锁的线程将被OS调度程序中断的风险越大。如果发生这种情况,则其他线程将保持“旋转”状态(反复尝试获取该锁),而持有该锁的线程在释放它方面并未取得进展。结果是无限期推迟,直到持有锁的线程可以完成并释放它为止。在单处理器系统上尤其如此,在该系统上,每个具有相同优先级的等待线程都可能浪费其量子时间(线程可以运行的分配时间)旋转,直到持有锁的线程最终完成。

特点

根据上面的描述可以总结自旋锁以下几个特点:

  • 在循环中,线程反复简单地等待(“旋转”)检查,直到锁可用为止。
  • 自旋锁(spin lock)是一种繁忙的等待,因为线程通过执行无用的任务(例如空for循环),而保持活动状态。
  • 当自旋锁在只自旋很短的时间时,效率非常高。
  • 如果自旋时间较长,可能会造成浪费。
和互斥锁比较

互斥锁的问题是使线程进入睡眠状态并再次唤醒它们都是相当耗时的操作,并且需要执行大量的CPU指令。如果现在互斥锁仅锁定很短的时间,则使线程进入睡眠状态并再次唤醒它所花费的时间可能会超过该线程实际睡眠的时间,甚至可能超过该线程将要执行睡眠的时间。不断轮询自旋锁浪费了CPU的时间,如果自旋锁保持更长的时间,则浪费更多的CPU时间,而如果线程处于睡眠状态会更好。

适用场景

在单核/单CPU系统上使用自旋锁是没有意义的,因为只要自旋锁轮询阻止了唯一可用的CPU内核,其他线程就无法运行,并且由于没有其他线程可以运行,因此锁不会被解锁。自旋锁只会浪费那些系统上的CPU时间,没有真正的好处。如果改为让该线程进入睡眠状态,则另一个线程可能会立即运行,可能会解除锁定,然后在第一个线程再次唤醒后允许其继续处理。

在多核/多CPU系统上,只有大量锁定仅在很短的时间内保持不变,浪费时间不断使线程进入睡眠状态并再次唤醒它们可能会显着降低运行时性能。当使用自旋锁时,线程有机会利用其完整的运行时长(始终仅在很短的时间段内阻塞,然后立即继续其工作),从而提高了处理吞吐量。

结论

由于开发人员是无法预先知道互斥或自旋锁是否会更好(例如,因为目标体系结构的CPU内核数量未知),因此操作系统也无法知道某个代码段是否已针对单核或Windows优化。在多核环境中,大多数系统并不严格区分互斥锁和自旋锁。实际上,大多数现代操作系统都具有混合互斥锁和混合自旋锁。

混合是什么意思?

混合互斥锁首先在多核系统上的行为就像自旋锁。如果线程无法锁定互斥锁,则不会立即进入休眠(sleep)状态,因为互斥锁可能很快就会被解锁,因此互斥锁将首先表现得像自旋锁。只有在一定时间(或重试或任何其他测量因素)之后仍未获得锁定时,线程才真正进入睡眠状态。如果相同的代码在只有一个内核的系统上运行,则互斥锁将不会自旋锁。

混合自旋锁起初的行为类似于普通自旋锁,但为避免浪费过多的CPU时间,它可能有一个后置(back-off) 策略(strategy)。它通常不会使线程立即进入睡眠状态(因为使用自旋锁时不会发生这种立即进入睡眠状态情况),但是它在一定情况下(在一定时间后停止)将会停止线程,允许另一个线程运行,因此增加了自旋锁被解锁(unlock)的机会(纯线程切换通常比涉及使线程进入休眠状态并稍后唤醒它更节省效率(waking it up))。

结尾

自旋锁不但出现在操作系统中,在编程中也经常出现,比如Atomic类中的自旋判断。实现方式如下

AtomicInteger.java

public final int getAndUpdate(IntUnaryOperator updateFunction) {
     int prev, next;
     do {
         prev = get();
         next = updateFunction.applyAsInt(prev);
     } while (!compareAndSet(prev, next));
     return prev;
 }

代码中的do-while语句块就是自旋锁的示例。

参考资料

When should one use a spinlock instead of mutex?(什么时候应该使用自旋锁而不是互斥锁?

What is a spin lock? (什么是自旋锁

What is spin lock?(什么是自旋锁

Spinlock(自旋锁

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值