AQS-lock【源碼分析】

AQS分析

lock.lock()過程

1.鎖的基本要素:一個共享的的數據來記錄鎖的狀態,AQS採用State作爲鎖標記,0爲無鎖,大於等於1是有鎖狀態
2.NonfairSync類代碼片段

        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

(1)獲得鎖成功:
compareAndSetState是CAS->樂觀鎖,原子性

protected final boolean compareAndSetState(int expect, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

即將expect和stateOffset相比如果相等則更新爲update否則不更新,整個過程是原子的
compareAndSetState(0, 1)爲true則獲得鎖成功執行,即將當前線程獲得獨佔鎖:
setExclusiveOwnerThread(Thread.currentThread());

protected final void setExclusiveOwnerThread(Thread thread) {
      	  exclusiveOwnerThread = thread;
}

(2)獲得鎖失敗:
如果獲得鎖失敗則走else,acquire(1);

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

①首先分析tryAcquire(arg):

protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
 final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

首先獲取一次鎖的狀態,如果爲無鎖狀態則採用CAS將當前線程設置爲有鎖狀態,返回true
這樣好處在於,線程執行效率如果快的話,再比較一次可能更加的有效率,每一個線程都可以在這再嘗試一次是否可獲得鎖,這也是一種插隊機制。

若不爲無鎖狀態,則進行判斷是否爲重入鎖,如果是則走else if一下邏輯,此段主要是對重入鎖進行重入次數的計數並設置State的值

若以上兩段都不滿足則返回false,並且由於!tryAcquire(arg)所以走到了這一段代碼acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

②分析acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
首先分析addWaiter(Node.EXCLUSIVE) 參數說明:Node.EXCLUSIVEnull arg1

private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
//嘗試快速詢問,故障時備份到完整的詢問
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

先來分析enq(node)

 private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
}

For(;;)代表死循環,以下圖爲例說明:
在這裏插入圖片描述

在第一次循環時,tail==null,所以走第一個if,然後緊接着CAS比較Head是否和null相等,如果相等則將Head設置爲一個空的Node即Node(){} ;所以如上圖step2所示,head指向了一個空節點Node,最後將tail = head。

在第二次循環時,tail已不爲空(指向空節點),走else語句:代碼如下所示

node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;

node爲形參傳入的node,它包裝了那個線程,例如ThreadB,以ThreadB爲例演示以下過程,首先node.prev指向t; CAS比較t跟內存中的tail是否相等,若相等則將tail更新爲node即指向node,t.next=node,返回t;請對照上圖step3看

若ThreadC進來,則不會進入enq(node)方法了,直接在以下代碼處返回

 private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail; //此處爲ThreadC進來
        if (pred != null) { //此處不爲空
            node.prev = pred; //將node的prev指向上一個尾節點
            if(compareAndSetTail(pred, node)){ //將尾節點換成當前節點Node(ThreadC)
                pred.next = node;  //pred的next指向Node(ThreadC)
                return node;		//返回Node(ThreadC)
            }
        }
        enq(node);
        return node;
    }

此時會返回到上面的②acquireQueued(addWaiter(Node.EXCLUSIVE), arg)),則第一個Thread傳遞的爲acquireQueued(Node(ThreadB),1);現在開始分析acquireQueued:

 final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {		//首先是死循環
                final Node p = node.predecessor(); //在下面進行分析1),返回prev節點
                if (p == head && tryAcquire(arg)) { //若第一次p==head爲true 2)
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) && //下面分析這個if 3)
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
}

1)node.predecessor()代碼:

final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

判斷prev是否爲null,若是報錯,否則返回p

2)tryAcquire代碼:再次去嘗試獲取鎖的那段代碼

 final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

3)shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()
1.shouldParkAfterFailedAcquire(p, node)

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
 int ws = pred.waitStatus; //ws默認爲0
 if (ws == Node.SIGNAL)   // SIGNAL爲-1
      return true;
 if (ws > 0) {			 // 大於0的只有CANCELLED==1
      do {
          node.prev = pred = pred.prev;
      } while (pred.waitStatus > 0);
      pred.next = node;
 } else {
      compareAndSetWaitStatus(pred, ws, Node.SIGNAL); //將狀態設置爲SIGNAL
 }
   return false;
}

2.parkAndCheckInterrupt()若想進入此方法,一定是第二個線程過來時

private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this); //將當前線程掛起
        return Thread.interrupted();	//返回是否被interrupt過,若是則返回true,並復位,默認爲false
}

此時線程被掛起,如下圖step4所示
在這裏插入圖片描述

由下面的lock.unlock()中的unpark()時
回到這段代碼

 final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) { //此時滿足,因爲在lock.unlock()執行unpark之後
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
}

tryAcquire(arg) ThreadB搶佔到鎖,並改變state和exclusiveOwnerThread爲自己,如下圖代碼所示:

if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
             }

分析這一段代碼

setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;

第一步:SetHead做的事情將Head指向ThreadB,將ThreadB中的thread屬性設置爲null,將指向之前的head節點設置爲指向null

private void setHead(Node node) {
        head = node;
        node.thread = null;
        node.prev = null;
    }

第二步:將p的next屬性設置爲null,最終如下圖所示:

在這裏插入圖片描述

lock.unlock()過程
猜測一下,首先釋放鎖肯定會將鎖狀態設置爲0,以及線程設置爲空,其次肯定會喚醒下一個等待的節點,使得去搶佔鎖
1 sync.release(1)

public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
      return false;
    }

1.1 tryRelease(arg)

 protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

將exclusiveOwnerThread設置爲null,將state設置爲0,即猜測第一步

1.2 繼續往下走

  Node h = head;
            if (h != null && h.waitStatus != 0) //滿足條件
                unparkSuccessor(h);
            return true;

1.3 unparkSuccessor(h)

private void unparkSuccessor(Node node) {
        int ws = node.waitStatus; 	// ws==-1
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0); //將head節點的waitstatus設置爲0
        Node s = node.next;					//s即爲ThreadB
        if (s == null || s.waitStatus > 0) {			//s不爲null
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread); 		//走到這,將ThreadB喚醒
    }

前面代碼遺留兩個問題
1

Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }

爲什麼要在尾部向後遍歷找到實際的,因爲可能會出現下圖情況
在這裏插入圖片描述

若從前往後找有可能會出現next沒引用上的問題

2
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
在此處被interrupt中斷後,在這裏相應:

static void selfInterrupt() {
        Thread.currentThread().interrupt();
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章