可重新進入:一個線程可以連續多次獲得鎖 加鎖
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