ReentrantLock的加锁过程是怎么样的?
面试题~
1 个回答
希望有人告诉你开心比对错更重要。梅拾璎### ReentrantLock的加锁过程 #### 非公平锁加锁过程 在非公平锁中,`lock()` 方法的逻辑如下: 1. **初次尝试获取锁:** - 线程调用 `lock()` 方法,如果当前 `state` 为 0,表示没有线程占有锁,线程通过 CAS(compare-and-set)操作尝试获取锁,并设置当前线程为独占锁线程。 - 如果获取锁成功,设置独占线程并返回。 - 如果获取锁失败,调用 `acquire(1)` 方法进行后续操作。 2. **再次尝试获取锁:** - 在 `acquire(int arg)` 方法中,`tryAcquire(arg)` 尝试获取锁,如果失败,则将当前线程包装成 `Node` 加入同步队列尾部,并进行自旋或挂起。 ```java static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } } ``` 3. **入队操作:** - 在 `addWaiter(Node mode)` 方法中,当前线程被包装成 `Node` 并尝试快速加入同步队列尾部。如果失败则调用 `enq(node)` 方法强制入队。 ```java private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); Node pred = tail; if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } enq(node); return node; } private Node enq(final Node node) { for (;;) { Node t = tail; if (t == null) { if (compareAndSetHead(new Node())) tail = head; } else { node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } } ``` 4. **竞争锁的过程:** - 在 `acquireQueued(final Node node, int arg)` 方法中,当前线程在入队后,尝试获取锁。只有当当前节点是头节点且获取锁成功,才能跳出循环。否则通过 `shouldParkAfterFailedAcquire` 判断是否挂起当前线程。 ```java 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; failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } } private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; if (ws == Node.SIGNAL) return true; if (ws > 0) { do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; } ``` 5. **非公平锁的实现:** - 在 `nonfairTryAcquire(int acquires)` 方法中,当前线程尝试通过 CAS 获取锁,如果成功,设置当前线程为独占线程。如果当前线程已经持有锁,则增加重入次数。 ```java 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) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } ``` ### 公平锁的加锁过程 在公平锁中,`lock()` 方法的逻辑如下: 1. **直接调用 `acquire(1)`:** - 公平锁在 `lock()` 方法中直接调用 `acquire(1)`,没有插队的机会。 ```java static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } protected final boolean tryAcquire(int acquires) { return fairTryAcquire(acquires); } } ``` 2. **公平锁的实现:** - 在 `fairTryAcquire(int acquires)` 方法中,当前线程尝试通过 CAS 获取锁。如果队列为空或当前线程是头节点,则获取锁成功。否则,新线程将被加入队列尾部。 ```java 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; } ``` 3. **队列判断:** - 在 `hasQueuedPredecessors()` 方法中,判断当前队列是否为空或当前线程是否为头节点,从而保证公平性。 ```java public final boolean hasQueuedPredecessors() { Node t = tail; Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); } ``` ### 总结 在非公平锁中,线程可以通过多次尝试插队获取锁,避免过多的队列操作和上下文切换。在公平锁中,所有线程必须按照FIFO顺序获取锁,保证了获取锁的公平性。编辑于 2023-12-17 02:54 •IP属地 北京举报评论收藏
公众号进入网站体验更佳

