Java 自旋锁

最近看视频刚好看到关于 Java 自旋锁相关内容,结合实例代码做一个记录

本文参考地址:面试必备之深入理解自旋锁

什么是自旋锁

自旋锁是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。

如何实现自旋锁

简单例子 🌰

下面代码是一个简单的自旋锁例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SpinLock {
private AtomicReference<Thread> cas = new AtomicReference<Thread>();
public void lock() {
Thread current = Thread.currentThread();
// 利用CAS
while (!cas.compareAndSet(null, current)) {
// 等待获取线程锁
}
}

public void unlock() {
Thread current = Thread.currentThread();
cas.compareAndSet(current, null);
}
}

存在的问题:
1、某个线程持有锁时间过长时,会导致其他线程一直处于等到状态,导致徒增 CPU 消耗
2、存在不公平情况,即等待时间长的线程不一定能优先获得锁

可重入锁🔐 和 不可重入锁🔐

上述代码存在的问题:当线程 A 持有锁时,在线程 A 释放锁之前不可再次获得锁。而可重入锁需要保证线程 A 再次获取锁时,也有机会获得锁对象。

优化代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Cas {
private AtomicReference<Thread> cas = new AtomicReference<Thread>();
private int count;

public void lock() {
Thread currentThread = Thread.currentThread();
if (currentThread == cas.get()) {
count++;
return;
}
while (cas.compareAndSet(null, currentThread)) {
// 等待获取线程锁
}
}

public void unlock() {
Thread currentThread = Thread.currentThread();
if (currentThread == cas.get() && count > 0) {
count--;
} else { //当前线程重入数量为0,释放锁资源
cas.compareAndSet(currentThread, null);
}
}
}

增加了一个线程「锁」计数器,当 A 线程的重入数量为0时,释放当前锁资源。