首页 > 代码库 > ReentrantLock是如何阻塞等待线程的?
ReentrantLock是如何阻塞等待线程的?
1 public class Test_Lock { 2 static ReentrantLock lock = new ReentrantLock(); 3 static class Runner implements Runnable { 4 @Override 5 public void run() { 6 lock.lock(); 7 System.out.println(Thread.currentThread().getName()); 8 lock.unlock(); 9 } 10 11 } 12 public static void main(String[] args) { 13 lock.lock(); 14 Thread t1 = new Thread(new Runner()); 15 t1.start(); 16 Thread t2 = new Thread(new Runner()); 17 t2.start(); 18 lock.unlock(); 19 System.out.println(lock.getHoldCount()); 20 } 21 }
新建一个ReentrantLock对象,主线程第一次调用ReentrantLock/lock -> NonfairSync/lock
1 final void lock() { 2 if (compareAndSetState(0, 1)) 3 setExclusiveOwnerThread(Thread.currentThread()); //主线程成功获取锁。 4 else 5 acquire(1); //子线程等待 6 }
子线程1调用ReentrantLock/lock -> NonfairSync/lock -> AbstractQueuedSynchronizer/acquire(1)
1 public final void acquire(int arg) { 2 if (!tryAcquire(arg) && 3 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 4 selfInterrupt(); 5 }
1) -> NonfairSync/tryAcquire -> Sync/nonfairTryAcquire
主线程已经获得锁,所以返回false。
1 final boolean nonfairTryAcquire(int acquires) { 2 final Thread current = Thread.currentThread(); 3 int c = getState(); 4 if (c == 0) { 5 if (compareAndSetState(0, acquires)) { 6 setExclusiveOwnerThread(current); 7 return true; 8 } 9 } 10 else if (current == getExclusiveOwnerThread()) { 11 int nextc = c + acquires; 12 if (nextc < 0) // overflow 13 throw new Error("Maximum lock count exceeded"); 14 setState(nextc); 15 return true; 16 } 17 return false; 18 }
2) -> AbstractQueuedSynchronizer/addWaiter -> AbstractQueuedSynchronizer/enq
把子线程加入等待队列,此时队列为空,所以直接enq(node)。
1 private Node addWaiter(Node mode) { 2 Node node = new Node(Thread.currentThread(), mode); 3 // Try the fast path of enq; backup to full enq on failure 4 Node pred = tail; 5 if (pred != null) { 6 node.prev = pred; 7 if (compareAndSetTail(pred, node)) { 8 pred.next = node; 9 return node; 10 } 11 } 12 enq(node); 13 return node; 14 }
enq函数是死循环里面包含一个if else分支,
先进if分支,创建了一个没有数据的节点作为头结点,加入队列,
再入else分支,使用CAS将当前节点插入队列尾部(如果由于多线程的原因导致执行失败,则会继续下一次循环插入节点,直到插入成功)。
最后返回插入成入的节点。
1 private Node enq(final Node node) { 2 for (;;) { 3 Node t = tail; 4 if (t == null) { // Must initialize 5 if (compareAndSetHead(new Node())) 6 tail = head; 7 } else { 8 node.prev = t; 9 if (compareAndSetTail(t, node)) { 10 t.next = node; 11 return t; 12 } 13 } 14 } 15 }
3) -> AbstractQueuedSynchronizer/acquireQueued
该方法就是不停地判断阻塞线程的条件是否满足,一旦满足就阻塞线程。
还是个死循环,死循环内部包含两个if,第一个if,p == head 但是tryAcquire会失败(同1),所以第一个if不执行。
第二个if,-> AbstractQueuedSynchronizer/shouldParkAfterFailedAcquire
判断前驱节点的状态值(此时为0),所以会执行CAS设置前驱节点的状态为Node.SIGNAL(-1),并返回 false。
进入下一轮循环,再次 -> AbstractQueuedSynchronizer/shouldParkAfterFailedAcquire,此时返回true。
1 final boolean acquireQueued(final Node node, int arg) { 2 boolean failed = true; 3 try { 4 boolean interrupted = false; 5 for (;;) { 6 final Node p = node.predecessor(); 7 if (p == head && tryAcquire(arg)) { 8 setHead(node); 9 p.next = null; // help GC 10 failed = false; 11 return interrupted; 12 } 13 if (shouldParkAfterFailedAcquire(p, node) && 14 parkAndCheckInterrupt()) 15 interrupted = true; 16 } 17 } finally { 18 if (failed) 19 cancelAcquire(node); 20 } 21 } 22 23 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { 24 int ws = pred.waitStatus; 25 if (ws == Node.SIGNAL) 26 /* 27 * This node has already set status asking a release 28 * to signal it, so it can safely park. 29 */ 30 return true; 31 if (ws > 0) { 32 /* 33 * Predecessor was cancelled. Skip over predecessors and 34 * indicate retry. 35 */ 36 do { 37 node.prev = pred = pred.prev; 38 } while (pred.waitStatus > 0); 39 pred.next = node; 40 } else { 41 /* 42 * waitStatus must be 0 or PROPAGATE. Indicate that we 43 * need a signal, but don‘t park yet. Caller will need to 44 * retry to make sure it cannot acquire before parking. 45 */ 46 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); 47 } 48 return false; 49 }
然后 -> AbstractQueuedSynchronizer/parkAndCheckInterrupt
此时线程就阻塞在死循环中了。
1 private final boolean parkAndCheckInterrupt() { 2 LockSupport.park(this); 3 return Thread.interrupted(); 4 }
那么线程是如何被唤醒的呢?
ReentrantLock是如何阻塞等待线程的?