自旋鎖
阻塞或者喚醒一個Java線程需要操作系統切換CPU 狀態來完成,這種狀態轉換 需要耗費處理器時間
如果同步代碼塊到代碼過於簡單,狀態轉換到時間有kennel比用戶執行代碼到時間還長
在許多場景下,同步資源到鎖定時間短,爲了這小段時間切換線程,線程的掛起和恢復可能會讓系統得不償失,
這裏是爲了當前線程“ 稍等一下”, 我們需要讓當前線程進行自旋 ,如果自旋完成後前面鎖定同步資源的線程以及釋放了鎖,那麼當前線程就沒必要阻塞,而是直接獲取同步資源,從而避免線程的開銷
阻塞鎖和自旋鎖相反,阻塞鎖如果沒有拿到鎖,就會直接阻塞,知道被喚醒
阻塞鎖
阻塞鎖是指當線程嘗試獲取鎖失敗時,線程進入阻塞狀態,直到接收信號後被喚醒.(線程的狀態包括新建、就緒、運行、阻塞及死亡)在JAVA中,能夠喚醒阻塞線程的操作包括Object.notify, Object.notifyAll, Condition.signal, LockSupport.unpark(JUC中引入)
原理和源碼分析
在 jdk 1.5 及以上併發框架 Java.util.concurrent 的 atomic 下 都是自旋鎖實現的
AtomicInteger 的實現 :自旋鎖的世勳啊原理是CAS
AtomicInteger 中是調用底層unsafe 進行自增操作的源碼中的 do-while 循環就是一個自旋操作,如果修改過程中一踏線程競爭導致修改失敗,就在while 死循環,直至成功
package com.dimple.test;
/**
* 本實例演示下線程的自旋鎖的實現
*/
public class SpinLockDemo {
private static AtomicReference<Thread> atomicReference=new AtomicReference<>();
public void lock(){
Thread thread=Thread.currentThread();
while(!atomicReference.compareAndSet(null,thread)){
System.out.println(thread.getName()+"自旋鎖獲取失敗,重新獲取中");
}
}
public void unlock(){
Thread thread=Thread.currentThread();
atomicReference.compareAndSet(thread,null);
}
public static void main(String[] args) {
SpinLockDemo spinLockDemo=new SpinLockDemo();
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"嘗試獲取自旋鎖");
spinLockDemo.lock();
System.out.println(Thread.currentThread().getName()+"獲取自旋鎖成功");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
spinLockDemo.unlock();
System.out.println(Thread.currentThread().getName()+"釋放自旋鎖");
}).start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"嘗試獲取自旋鎖");
spinLockDemo.lock();
System.out.println(Thread.currentThread().getName()+"獲取自旋鎖成功");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
spinLockDemo.unlock();
System.out.println(Thread.currentThread().getName()+"釋放自旋鎖");
}).start();
}
}
Thread-0嘗試獲取自旋鎖
Thread-0獲取自旋鎖成功
Thread-1嘗試獲取自旋鎖
Thread-1自旋鎖獲取失敗,重新獲取中
Thread-1自旋鎖獲取失敗,重新獲取中
Thread-1自旋鎖獲取失敗,重新獲取中
Thread-1自旋鎖獲取失敗,重新獲取中
我們打開 AtomicInteger 源碼看一下,這裏是個 do while 循環
自旋鎖的應用場景
自旋鎖一般用於多核服務器,在併發度不是特別搞的情況下,比阻塞鎖效率高,
另外,自旋鎖適用於臨界區較小的情況,否則如果臨界區很大,(線程一旦拿到鎖,很多之後纔會釋放),那也是不適合的