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成功獲取到鎖,然後釋放

Tags: