公平锁:指的是多个线程等待同一个锁时,等待时间最长的线程将会优先获取到锁。但是,这也会导致当线程尝试获取锁时,很大概率会进入阻塞状态,有很高的状态切换成本,因此,会降低吞吐量和执行效率。
非公平锁:不会保证一个特定获取到锁的顺序。当线程尝试获取锁时,会优先通过CAS尝试获取锁,如果失败了才会进入等待队列。非公平锁的执行效率更高,因为线程尝试获取锁的时候,如果能够直接获取到锁,可以节省线程的上下文切换带来的时间和性能上的开销。但是,可能会导致有的线程永远也获取不到锁。
Java中的synchronized
是非公平锁,ReentrantLock
默认也是非公平锁,并且是可重入的。ReentrantLock
则可以通过fair
来设置为公平锁。下面是ReentrantLock
的类图结构:
公平锁加锁
公平锁加锁过程的方法调用:ReentrantLock#lock()
→ ReentrantLock.FairSync#lock()
→ AbstractQueuedSynchronizer#acquire(1)
。下面看下acquire的代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
static final class FairSync extends Sync { ......
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; } }
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; } 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); } }
|
有一点需要注意,即使ReentrantLock
构造时fair
设置为true,使用不带超时的tryLock()
加锁时也会使用非公平锁的策略。
非公平锁加锁
调用链路:ReentrantLock#lock()
→ ReentrantLock.NonfairSync#lock()
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| static final class NonfairSync extends Sync { ...... final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
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; }
|
从代码可以看出,公平锁和非公平锁的加锁流程几乎相同,区别是state==0时,非公平锁会直接使用CAS尝试加锁,而公平锁会首先判断等待队列中是否有其他线程在排队,如果没人在排队才会使用CAS加锁。
释放锁
公平锁和非公平锁释放锁的流程是一样的。调用链路ReentrantLock#unlock()
→ AbstractQueuedSynchronizer#release()
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; }
|