AQS詳解 1

1.本文不講概念,用jdk8調試一步一步搞清AQS這個東西,請耐心並跟着本文實操

現在我們先實現下面這個類:

public class MutexLock {
	static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 3601133938511592218L;
        /***
         *  嘗試獲取鎖
         */
        protected boolean tryAcquire(int arg) {
            // 當 state 值爲0時,然後就改爲1,否則就返回false也不修改state值
            if (compareAndSetState(0, 1)) {
                // 設置當前線程爲 獨佔線程
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
		//嘗試釋放鎖
        @Override
        protected boolean tryRelease(int arg) {
            if (getState() == 0) {
                // state 爲 0 說明當前同步塊中沒有鎖了,無需釋放
                throw new IllegalMonitorStateException();
            }
            //當前獨佔線程 設置爲null
            setExclusiveOwnerThread(null);
            // 將狀態變量的值設爲 0,以便其他線程可以成功修改狀態變量從而獲得鎖
            setState(0);
            return true;
        }

        // 當前線程是否被獨佔
        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }
    }
    // 將操作代理到 Sync 上
    private final Sync sync = new Sync();

    public void lock() {
        sync.acquire(1);
    }
    /**
     * 試圖釋放此鎖
     */
    public void unlock() {
        sync.release(1);
    }

上面代碼仿照了ReentrantLock,實現了lock和unlock方法,實際操作都是Sync這個內部類操作的。
下面我們來測試下:

 public static void main(String[] args) throws InterruptedException {
        // final ReentrantLock lock = new ReentrantLock();
        final MutexLock lock = new MutexLock();
        ///  代碼A
        lock.lock(); 
        new Thread() {
            public void run() {
                //代碼B
                lock.lock();
                System.err.println("==============");
                lock.unlock();
            };
        }.start();
        Scanner sc = new Scanner(System.in);
        System.out.print("Please enter a string : ");
        String lein = sc.next();
        if (lein.equals("1")) {
            //代碼C
            lock.unlock();
        }
    }

上面代碼大致解釋:

  1. 主線程加鎖,然後子線程lock時拿不到鎖會阻塞等待
  2. 主線程在控制檯輸入1 回車時,主線程unlock,子線程被喚醒直到執行完畢!

調試分析

1.代碼A 塊的執行

我們調用代碼A 【lock.lock();】 方法,跟蹤進到AQS類的acquire方法
在這裏插入圖片描述
執行子類的tryAcquire方法, 調試我們返回true。
在這裏插入圖片描述

返回上層函數,直接返回,到此main函數的代碼A 【lock.lock();】 方法已經執行完畢!在這裏插入圖片描述
執行完總結:
AQS的兩個成員變量
在這裏插入圖片描述
在這裏插入圖片描述
state 的值 變成1。
exclusiveOwnerThread的值變成 當前main函數線程的引用。

2.代碼B塊的執行

同樣跟蹤到下面tryAcquire方法,整個方法返回false
在這裏插入圖片描述
回到上層方法,進入addWaiter
在這裏插入圖片描述
addWaiter方法如下
在這裏插入圖片描述
執行enq方法之前我們觀察Node的數據結構和值如下(記住)
在這裏插入圖片描述
進入enq方法,進入第一次循環。
在這裏插入圖片描述

第二次序號循環在這裏插入圖片描述
到此我們能想象出如下數據結構圖
在這裏插入圖片描述
到此我們有了Node1節點Node2節點

繼續走代碼
在這裏插入圖片描述
返回上一層,執行acquireQueued函數在這裏插入圖片描述
acquireQueued函數的第一次循環在這裏插入圖片描述

下面我們看shouldParkAfterFailedAcquire這個方法
在這裏插入圖片描述

上面執行完後,需要知道Node1節點的waitStatus變量變成-1在這裏插入圖片描述

第二次循環,上面shouldParkAfterFailedAcquire返回false,所以直接進入第二次循環
在這裏插入圖片描述
第二次進入shouldParkAfterFailedAcquire方法
在這裏插入圖片描述
返回上一層在這裏插入圖片描述
進入parkAndCheckInterrupt方法
在這裏插入圖片描述
在這裏插入圖片描述

代碼B塊的執行總結
1.阻塞了當前子線程,利用LockSupport.park工具阻塞
2.在AQS內部構造瞭如下鏈表數據結構
在這裏插入圖片描述
今天就講到這裏吧,分析不清楚的敬請諒解。可以加我qq一起討論。
老生常談:深圳有愛好音樂的會打鼓(吉他,鍵盤,貝斯等)的程序員和其它職業可以一起交流加入我們樂隊一起嗨。我的QQ:657455400 表演視頻實例https://v.qq.com/x/page/f0517awx0x4.html

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