java源碼分析:重入鎖ReentrantLock

來源:互聯網
上載者:User

可重新進入:一個線程可以連續多次獲得鎖 加鎖

Lock.lock() 擷取鎖

        final void lock() {            acquire(1);        }

很簡單,就是調用acqire函數,後面的參數1表示,如果獲得了鎖,對於另外進程釋放而擷取擷取的,那麼就鎖的值就是1,否則如果是重入的值+1

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

首先通過tryAcquire擷取鎖,如果沒有擷取,那麼首先通過addWaiter把當前線程放到等待隊列的最後面,並阻塞它。

而在tryAcquire中,根據是否公平鎖有兩個版本,先看公平鎖

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

這個函數就是嘗試去擷取鎖,返回是否擷取了鎖。首先擷取鎖的狀態,看是否為0,如果是0,那麼當前是沒有線程佔據鎖的,那麼判斷當前線程是否在隊列的第一個,如果是那麼通過原子的compareAndSetState擷取鎖,把當前線程設定為鎖的獲得者,返回true。如果當前鎖是被線程佔領的,那麼看下是不是被當前線程佔了,如果是的話,把對應的持有鎖的次數+acquires。 如果沒有成功擷取鎖,那麼就會調用addWaiter和acquireQueued兩個函數,他們兩個分別是把線程放到了鎖的隊列最後面,然後把線程給阻塞了。

    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);        }    }

這塊代碼不斷迴圈做兩件事,第一件判斷當前線程是不是隊列的最前面,如果是的話,那麼再次嘗試擷取鎖,成功就把當前節點設定為頭並返回是否被中斷過。 否則就幹第二件事,調用shouldParkAfterFailedAcquire決定是否把當前進行給阻塞,如果需要那麼通過parkAndCheckInterrupt把線程阻塞,阻塞成功把interrupted設定true。

我們在來看下shouldParkAfterFailedAcquire這個函數

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {        int ws = pred.waitStatus;        if (ws == Node.SIGNAL)            /*             * This node has already set status asking a release             * to signal it, so it can safely park.             */            return true;        if (ws > 0) {            /*             * Predecessor was cancelled. Skip over predecessors and             * indicate retry.             */            do {                node.prev = pred = pred.prev;            } while (pred.waitStatus > 0);            pred.next = node;        } else {            /*             * waitStatus must be 0 or PROPAGATE.  Indicate that we             * need a signal, but don't park yet.  Caller will need to             * retry to make sure it cannot acquire before parking.             */            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);        }        return false;    }

首先看當前線程的前個節點的狀態,如果是SIGNAL,那麼他們都沒有擷取鎖,那就直接返回true表示進入阻塞狀態

其次:把前面節點中是cancelled狀態的給刪除了,把當前節點作為第一個非cancelled進程的下遊

否則就設定前繼的狀態為SIGNAL,這樣下次迴圈可以進入1而進入阻塞

如果需要阻塞,通過下面函數完成

 private final boolean parkAndCheckInterrupt() {        LockSupport.park(this);        return Thread.interrupted();    }

阻塞,如果阻塞結束後,返回線程的中斷狀態。

# 參考

http://blog.csdn.net/yanyan19880509/article/details/52345422

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.