非公平锁 lock 方法实现
特别说明一下:下面会涉及到非公平/公平锁的解析,在解析之前我会加粗说是那种方式的锁。
acquire()
acquire
方法在 AbstractQueuedSynchronizer
类中,该方法就是去尝试获取锁,如果获取失败了,就会尝试再次获取,或者进入 AQS 队列中。
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
主要有三条流程(本质就是 3 个核心方法) tryAcquire
、 addWaiter
、acquireQueued
FairSync#tryAcquire
1、公平锁tryAcquire
是由子类 FairSync 实现, 这里和非公平锁的区别就在于调用了 hasQueuedPredecessors
方法判断是否有线程在排队。
protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if ( // hasQueuedPredecessors 这里就是公平锁的体现,如果人在排队就不能获取锁,需要先进入排队,所以性能上来说比非公平锁要一些。 !hasQueuedPredecessors() && // cas 获取锁 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; }
2、addWaiter
操作,顾名思义就是当前线程尝试获取锁失败,然后进入将当前 Thread 封装为一个 AQS Node 节点,进入等待队列排队
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; }
如果没有尾节点会调用 enq
进行初始化头节点和入队操作:
private Node enq(final Node node) { for (;;) { Node t = tail; // 首次初始化 if (t == null) { // Must initialize if (compareAndSetHead(new Node())) tail = head; } else { // 加入当前节点 node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }
3、调用acquireQueued
方法是在,当前获取锁的线程进入 AQS 之后进入阻塞之前执行的方法,其实就是在进入等待之前做最后一次 “抢救” 尝试能否获取锁。还有一个逻辑就是在 AQS 唤醒过后,当前获取到锁的节点就成了头节点,会将 "旧" 头节点丢弃掉。
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); } }
NonfairSync#tryAcquire()
1、 本次走非公平锁 NonfairSync
, 非公平锁对比公平锁没有是否有队列的排队的判断,只要是锁获取的参与者,都可以直接进行锁的竞争。
// 首先我们先看 `tryAcquire()` 方法 protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } // 核心是调用 `nonfairTryAcquire(acquires)` final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); // 如果可以获取锁 if (c == 0) { // 修改 state if (compareAndSetState(0, acquires)) { // 设置锁的持有者(持有线程) setExclusiveOwnerThread(current); return true; } } // 重入支持 else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
2、 addWaiter(Node.EXCLUSIVE)addWaiter
方法实现
如果前面没有排队线程将调用 enq
方法
双向链表中,第一个节点为虚节点(也叫做哨兵节点),其实并不存储任何信息,只是占位,真正的第一个有数据的节点,是从第二个节点开始的。
acquireQueued(addWaiter(Node.EXCLUSIVE))
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; } // shouldParkAfterFailedAcquire 第 1 次返回 false // 第 2 次返回 false if (shouldParkAfterFailedAcquire(p, node) && // 内部会阻塞线程调用 LockSupport.park(this); parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } } // setHead 方法 // 就是把当前获取锁的节点设置为哨兵节点,然后之前旧哨兵节点设置为 null private void setHead(Node node) { head = node; node.thread = null; node.prev = null; }