Java自旋锁
自旋锁(SpinLock)是一种基于忙等待的锁机制,它不会将线程阻塞,而是通过循环不断检查锁的状态来实现同步。在多核处理器上,自旋锁的性能通常比互斥锁要好,因为线程不需要切换到阻塞状态。然而,在单核处理器上,自旋锁可能会导致线程一直处于忙等待状态,浪费 CPU 资源。
原理
自旋锁的核心思想是通过不断循环检查锁的状态来获取锁。当一个线程想要获取自旋锁时,它首先会尝试原子地将锁的状态从未锁定变为锁定状态,如果成功则表示获取到了锁,否则会一直循环检查锁的状态直到获取到为止。
自旋锁的优点是适用于锁持有时间非常短的情况,因为线程不需要切换上下文,而且自旋锁的实现通常比互斥锁简单高效。然而,自旋锁的缺点是会占用大量的 CPU 资源,因为线程一直在忙等待,如果锁的持有时间很长,那么自旋锁可能会导致线程一直处于忙等待状态,浪费 CPU 资源。
使用示例
下面我们通过一个简单的示例来演示如何使用Java中的自旋锁。
import java.util.concurrent.atomic.AtomicBoolean;
public class SpinLock {
private AtomicBoolean locked = new AtomicBoolean(false);
public void lock() {
while (!locked.compareAndSet(false, true)) {
// 自旋等待获取锁
}
}
public void unlock() {
locked.set(false);
}
}
在上述示例中,我们使用java.util.concurrent.atomic.AtomicBoolean
类来表示锁的状态,false
表示未锁定,true
表示已锁定。lock()
方法通过循环调用compareAndSet()
方法来尝试获取锁,如果成功则返回true
,表示获取到了锁,否则返回false
,继续循环等待。unlock()
方法则将锁的状态设置为false
,表示释放了锁。
下面是一个使用自旋锁的示例:
public class SpinLockDemo {
private static int count = 0;
private static SpinLock spinLock = new SpinLock();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
spinLock.lock();
count++;
spinLock.unlock();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
spinLock.lock();
count--;
spinLock.unlock();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("count: " + count);
}
}
在上述示例中,我们创建了两个线程t1
和t2
,分别对count
进行加1和减1的操作。在每次操作之前,线程会先获取自旋锁,然后进行操作,最后释放锁。最后,我们打印出count
的值,如果自旋锁的实现正确,那么最终输出的结果应该是0。
旅行图
下面是自旋锁的旅行图:
journey
title 自旋锁的旅行图
section 自旋锁的旅行过程
[*]-->获取到锁: 线程循环检查锁的状态直到获取到锁
获取到锁-->操作数据: 线程获取到锁之后进行操作
操作数据-->释放锁: 操作完成后释放锁
释放锁-->[*]: 线程继续循环检查锁的状态
状态图
下面是自旋锁的状态图