自旋鎖與阻塞鎖

自旋鎖

阻塞或者喚醒一個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 循環
在這裏插入圖片描述

自旋鎖的應用場景

自旋鎖一般用於多核服務器,在併發度不是特別搞的情況下,比阻塞鎖效率高,
另外,自旋鎖適用於臨界區較小的情況,否則如果臨界區很大,(線程一旦拿到鎖,很多之後纔會釋放),那也是不適合的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章