大厂经常会自旋锁相关的问题,比如:什么是自旋锁?如何实现自旋锁?底层原理?下面全面详解自旋锁@mikechen
自旋锁
自旋锁:是一种轻量级锁,线程在获取不到锁时不会阻塞,而是通过不断循环检查锁状态(自旋),直到成功获取锁。
线程在等待锁释放期间,不会放弃CPU执行权,而是不断地循环检查锁的状态。
适用于:短时间持有锁的场景,以减少线程上下文切换的开销。
自旋锁实现原理
自旋锁的核心思想是:让线程在等待锁的过程中“忙等待”,即不断地循环检查锁的状态,而不是立即进入阻塞状态。
自旋锁实现,可以基于 CAS(Compare-And-Swap) 和 CPU 指令级原子操作,通过不断轮询锁状态来尝试获取锁,而不进入阻塞。
CAS 是一种无锁同步机制,可以在多线程环境下保证变量的安全更新。
CAS(Compare-and-Swap,比较并交换)是一种原子操作,它在多线程并发环境中用于实现线程同步。
boolean compareAndSwap(int *V, int E, int N) { if (*V == E) { // 如果当前值等于期望值 *V = N; // 则更新为新值 return true; // 返回成功 } return false; // 否则更新失败 }
CAS 操作包含三个值:
V(当前值);
E(期望值);
N(新值);
只有当 V == E
时,才会将 V
更新为 N
,否则更新失败,并重新尝试(自旋)。
CAS操作的核心思想是:
比较:
将内存中的一个值与预期值进行比较。
交换:
如果内存中的值与预期值相等,则将内存中的值更新为新的值。
如果内存中的值与预期值不相等,则不进行任何操作。
如下所示:
import java.util.concurrent.atomic.AtomicReference; public class SpinLock { private AtomicReference<Thread> owner = new AtomicReference<>(); public void lock() { Thread currentThread = Thread.currentThread(); while (!owner.compareAndSet(null, currentThread)) { // 自旋等待 } } public void unlock() { Thread currentThread = Thread.currentThread(); owner.compareAndSet(currentThread, null); } }
首先,尝试获取锁。
如果锁未被占用,则当前线程成功获取锁。
如果锁已被其他线程占用,进入自旋(不断尝试)。
其次,自旋等待。
线程不断尝试获取锁,而不会进入阻塞状态(即不会触发线程切换)。
如果锁很快释放,线程可以立即获取到锁,避免线程上下文切换的开销。
其次,成功获取锁后执行任务。
线程获取锁后,进入临界区,执行临界资源操作。
最后,释放锁。
任务完成后,线程使用 CAS 操作 释放锁,使其他线程可以获取锁。
自旋锁某些情况下效率很高,对于锁持有时间短的场景,自旋锁可以显著提高性能,因为它避免了线程阻塞、和唤醒的开销。
但是,如果锁的持有时间过长,自旋会导致线程长时间占用CPU,造成资源浪费。