自旋一词来源于CAS,即compareandSet(比较和交换),CAS实现基础应在原子引用或者操作原子Atomic类型之上,意思就是线程通过不断循环的方式来获取锁,
若读完不懂或没读就不懂,请复习JMM内存模型+volatile+java.util.concurrent.Atomic再来.
package com.reentralock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* 自旋锁 线程a调lock方法 自己持有锁5s,b在5s内不断尝试获取锁
*
* 自旋的基础是Atomic,所以一定要用到原子引用
*/
public class SpinLock {
AtomicReference<Thread> reference = new AtomicReference<>();//此时构造方法未传入值,此时原子线程默认是null
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"\t come in");
//自旋的精髓是循环
while(!reference.compareAndSet(null, thread)){//
System.out.println(Thread.currentThread().getName()+"\t 循环尝试获取锁");
}
}
public void myUnLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"\t unlock");
reference.compareAndSet(thread, null);//原子操作+期望值吻合+释放
}
public static void main(String[] args) {
SpinLock spinLock =new SpinLock();
new Thread(()->{
spinLock.myLock();//aa获取锁
try {
TimeUnit.SECONDS.sleep(5);//aa持有锁5s
} catch (Exception e) {
e.printStackTrace();
}
spinLock.myUnLock();//aa释放锁
}, "aa").start();
try {
TimeUnit.SECONDS.sleep(1);//保证线程aa先获取到锁
} catch (Exception e) {
e.printStackTrace();
}
new Thread(()->{
spinLock.myLock();//bb获取锁
System.out.println("bb终于得到锁了");
spinLock.myUnLock();//bb释放锁
}, "bb").start();
}
}
当aa线程获取主内存的共享变量reference的副本到自己的工作内存且reference=null时,null为aa线程的期望值,若为null,aa线程在自己的工作内存中将reference=null改成reference=aa线程,aa线程执行时间为5s(在5s过程中,bb线程启动),5s后aa线程调unlock方法将reference=aa改为reference=null,并将reference=null写回主内存中;bb线程在aa线程启动的1s后,不断获取主存中reference的值,但在aa线程没执行完之前,主内存的reference始终=aa线程,故bb执行while方法不断循环获取主内存的reference,直到aa线程执行5s完毕,主存的reference=null;线程bb如愿以偿的获取到了锁,打印了“bb终于得到锁了”这句话,后释放了锁,将reference恢复到初始值:reference=null,也就是最初的代码AtomicReference<Thread> reference = new AtomicReference<>();。
总结:自旋锁其实就是线程aa获取到锁,bb线程在获取锁的时候不是处于阻塞状态在等待队列中一直等,而是不断循环的获取锁,但是如果说aa执行时间特别长,bb还是在不断获取,因为每次获取都需要一点内存来执行,这样会大量耗费cpu的资源,影响性能,应根据情况使用。