AbstractQueuedSynchronizer:獨佔鎖

併發是java編程一個比較高級的一個點,自己也常常試用,但是對於併發怎麼實現了,確實不明白,打算認真學習下,從AbstractQueuedSynchronizer開始吧,自己水平有限,如果有不對的地方,麻煩指出來.

AbstractQueuedSynchronizer是Countdownlatch,ReentrantLock,ThreadPoolExecutor,ReentrantReadWriteLock,Semaphore的基礎.而AbstractQueuedSynchronizer繼承AbstractOwnableSynchronizer.

AbstractOwnableSynchronizer

A synchronizer that may be exclusively owned by a thread. This class
provides a basis for creating locks and related synchronizers that may
entail a notion of ownership. The AbstractOwnableSynchronizer class
itself does not manage or use this information. However, subclasses
and tools may use appropriately maintained values to help control and
monitor access and provide diagnostics.

我的理解是,那個線程獲得cpu或者臨界資源吧.只有一個參數,Thread exclusiveOwnerThread;

Node

 static final class Node {
        static final Node SHARED = new Node();
        static final Node EXCLUSIVE = null;
        static final int CANCELLED =  1;
        static final int SIGNAL    = -1;
        static final int CONDITION = -2;
        static final int PROPAGATE = -3;
        volatile int waitStatus;
        volatile Node prev;
        volatile Node next;
        volatile Thread thread;
        Node nextWaiter;

        final boolean isShared() {
            return nextWaiter == SHARED;
        }

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

        Node() {    
        }

        Node(Thread thread, Node mode) {    
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { 
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }
  • Node prev; 前驅節點
  • Node next; 後繼節點。
  • Thread thread; 入隊列時的當前線程。
  • waitStatus:表示節點的狀態。其中包含的狀態有:
    CANCELLED,值爲1,表示當前的線程被取消;
    SIGNAL,值爲-1,表示當前節點的後繼節點包含的線程需要運行,也就是unpark;
    CONDITION,值爲-2,表示當前節點在等待condition,也就是在condition隊列中;
    PROPAGATE,值爲-3,表示當前場景下後續的acquireShared能夠得以執行;
    值爲0,表示當前節點在sync隊列中,等待着獲取鎖。
  • Node nextWaiter

    Link to next node waiting on condition, or the special value SHARED. Because condition queues are accessed only when holding in exclusive mode, we just need a simple linked queue to hold nodes while they are waiting on conditions. They are then transferred to the queue to re-acquire. And because conditions can only be exclusive, we save a field by using special value to indicate shared mode.
    存儲condition隊列中的後繼節點。表示了兩種不同的模式:
    1)共享模式(SHARED,允許多個線程通過);
    2)排它模式(EXCLUSIVE,只允許一個線程通過)。

  • Node predecessor();前任節點

    類的成員變量

  • Node head:Head of the wait queue, lazily initialized. Except for initialization, it is modified  only via method setHead. Note: If head exists, its waitStatus is guaranteed not to be    CANCELLED.

  • Node tail:Tail of the wait queue, lazily initialized. Modified only via method enq to add new wait node.
  • int state:The synchronization state.(標誌臨界資源是否獲取,1:已經唄獲取了,0:沒有被獲取)

    獨佔鎖(EXCLUSIVE)

    acquire

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
  • tryAcquire:排它的獲取這個狀態。這個方法的實現需要查詢當前狀態是否允許獲取,然後再進行獲取(使用compareAndSetState來做)狀態。

  • addWaiter:把當前線程節點放到隊列尾

    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;
    }
  • acquireQueued
    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)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

這裏針對acquire做一下總結:
1. 狀態的維護;
需要在鎖定時,需要維護一個狀態(int類型),而對狀態的操作是原子和非阻塞的,通過同步器提供的對狀態訪問的方法對狀態進行操縱,並且利用compareAndSet來確保原子性的修改。
2. 狀態的獲取;
一旦成功的修改了狀態,當前線程或者說節點,就被設置爲頭節點。
3. sync隊列的維護。
在獲取資源未果的過程中條件不符合的情況下(不該自己,前驅節點不是頭節點或者沒有獲取到資源)進入睡眠狀態,停止線程調度器對當前節點線程的調度。

###release

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

主要看下unparkSuccessor:如果存在後續節點,喚醒

    private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
        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;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }

可以看出來,獲取鎖的節點,一直在隊列頭部

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