自旋锁

是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好吃是减少线程上下文切换的消耗,缺点是循环会消耗CPU。

自旋的方式我们在查看AtomicInteger的底层时候就已经体验过了

image-20200926154805922

1
2
3
4
5
6
7
8
9
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
//自旋
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

return var5;
}

手写自旋锁

首先原子引用线程类AtomicReference<Thread> atomicReference = new AtomicReference<>();

接着编写myLock()首先通过Thread.currentThread();获取到当前线程thread,接着通过原子引用的compareAndSet(null,thread);比较预期值和真实值是否为null,如果为null则更新值为thread,返回true,取反为false退出循环。否则不为null则返回为false,取反为true一直循环,直到为null。从而达到自旋的效果!

myUnLock方法则也是一开始通过Thread.currentThread();获取到当前线程thread,接着通过原子引用的compareAndSet(thread,null);比较预期值和真实值是否为同一个threadl,相同则更新成功为null。(这里解锁方法没有完善,线程没有上锁直接解锁也可以正常执行该方法打印语句退出)

image-20200926160241980

这里优化一下,我们通过对compareAndSet(thread,null);的结果取反做为if的判断条件,只有当compareAndSet(thread,null);的结果为false时,也就是期望与真实值不符时抛出运行时异常throw new RuntimeException("解锁出现了异常!");

image-20200926161022010

当我们直接不上锁而解锁时抛出异常

image-20200926161148545

测试成功,开始正常情况测试!

image-20200926161344412

通过主线程的睡眠,确保每次都是线程A执行,线程A首先获取到自旋锁,接着暂停5秒,在这5秒期间线程B想获取到锁,但是此时的锁在线程A中经过while循环判断不符合,所以线程B一直处于采用循环的方式去尝试获取锁。知道线程A继续开始执行,释放了锁。线程B才能获取到锁并后续将它释放!!

运行验证结果~

image-20200926161735146

代码测试正确!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class SpinLockDemo {
//原子引用线程类
AtomicReference<Thread> atomicReference = new AtomicReference<>();

public static void main(String[] args) {
SpinLockDemo spinLockDemo = new SpinLockDemo();

new Thread(() -> {
spinLockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
spinLockDemo.myUnLock();
}, "AA").start();

//保证每次AA线程先执行,BB后执行
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}

new Thread(() -> {
spinLockDemo.myLock();
spinLockDemo.myUnLock();
}, "BB").start();

}

public void myLock() {
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "\t come in");

while (!atomicReference.compareAndSet(null, thread)) {

}
}

public void myUnLock() {
Thread thread = Thread.currentThread();
if (!atomicReference.compareAndSet(thread, null)){
throw new RuntimeException("解锁出现了异常!");
}
System.out.println(Thread.currentThread().getName() + "\t invoked myUnlock()");
}
}