Java鎖之自旋鎖
Java鎖之自旋鎖
自旋鎖:spinlock,是指嘗試獲取鎖的執行緒不會立即阻塞,而是採用循環的方式去嘗試獲取鎖,這樣的好處是減少執行緒上下文切換的消耗,缺點是循環會消耗CPU
原來提到的比較並交換,底層使用的就是自旋,自旋就是多次嘗試,多次訪問,不會阻塞的狀態就是自旋。
優缺點
優點:循環比較獲取直到成功為止,沒有類似於wait的阻塞
缺點:當不斷自旋的執行緒越來越多的時候,會因為執行while循環不斷的消耗CPU資源
手寫自旋鎖
通過CAS操作完成自旋鎖,A執行緒先進來調用myLock方法自己持有鎖5秒,B隨後進來發現當前有執行緒持有鎖,不是null,所以只能通過自旋等待,直到A釋放鎖後B隨後搶到
/**
* 手寫一個自旋鎖
*
* 循環比較獲取直到成功為止,沒有類似於wait的阻塞
*
* 通過CAS操作完成自旋鎖,A執行緒先進來調用myLock方法自己持有鎖5秒,B隨後進來發現當前有執行緒持有鎖,不是null,所以只能通過自旋等待,直到A釋放鎖後B隨後搶到
*/
public class SpinLockDemo {
// 現在的泛型裝的是Thread,原子引用執行緒
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void myLock() {
// 獲取當前進來的執行緒
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "\t come in ");
// 開始自旋,期望值是null,更新值是當前執行緒,如果是null,則更新為當前執行緒,否者自旋
while(!atomicReference.compareAndSet(null, thread)) {
}
}
/**
* 解鎖
*/
public void myUnLock() {
// 獲取當前進來的執行緒
Thread thread = Thread.currentThread();
// 自己用完了後,把atomicReference變成null
atomicReference.compareAndSet(thread, null);
System.out.println(Thread.currentThread().getName() + "\t invoked myUnlock()");
}
public static void main(String[] args) {
SpinLockDemo spinLockDemo = new SpinLockDemo();
// 啟動t1執行緒,開始操作
new Thread(() -> {
// 開始佔有鎖
spinLockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 開始釋放鎖
spinLockDemo.myUnLock();
}, "t1").start();
// 讓main執行緒暫停1秒,使得t1執行緒,先執行
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 1秒後,啟動t2執行緒,開始佔用這個鎖
new Thread(() -> {
// 開始佔有鎖
spinLockDemo.myLock();
// 開始釋放鎖
spinLockDemo.myUnLock();
}, "t2").start();
}
}
最後輸出結果
t1 come in
.....五秒後.....
t1 invoked myUnlock()
t2 come in
t2 invoked myUnlock()
首先輸出的是 t1 come in
然後1秒後,t2執行緒啟動,發現鎖被t1佔有,所有不斷的執行 compareAndSet方法,來進行比較,直到t1釋放鎖後,也就是5秒後,t2成功獲取到鎖,然後釋放