自旋一詞來源於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的資源,影響性能,應根據情況使用。