一、经典故事
村子里面,有一口井水。水质很好,村民们都想打井里的水。
村长这时就制定了规则:
井边安排一个看井人,维护打水的秩序。
打水时,以家庭为单位,哪个家庭任何人先到井边,就可以先打水,而且如果一个家庭占到了打水权,其家人这时候过来打水不用排队。
而那些没有抢占到打水权的人,一个一个挨着在井边排成一队,先到的排在前面。
二、图析ReentrantLock
/** * Creates an instance of {@code ReentrantLock}. * This is equivalent to using {@code ReentrantLock(false)}. */ public ReentrantLock() { sync = new NonfairSync(); } /** * Creates an instance of {@code ReentrantLock} with the * given fairness policy. * * @param fair {@code true} if this lock should use a fair ordering policy */ public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
由构造函数中可知,ReentrantLock默认采用的是非公平锁模式,之所以默认才用这种模式,是因为:
非公平锁减少线程挂起的几率,后来的线程有一定的几率直接获取锁。
1. 非公平模式:
final void lock() { if (compareAndSetState(0, 1)) // CAS尝试获取锁权 setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 这里区别公平锁,直接CAS再次尝试获取锁权 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. 公平锁模式:
final void lock() { // 按部就班,看排队情况 acquire(1); } public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 前面没人排队才CAS尝试获取锁 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; }
至于:
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
acquireQueued(addWaiter(Node.EXCLUSIVE), arg):入队
selfInterrupt():阻塞
三、比对打水的故事
问看井人:空闲情况下,所有家庭问的结果,只能有一个成功。(CAS)
可重入锁:家庭为单位(每一个人同属一个线程),家庭获取到了打水权,家庭里所有人都获取到了打水权。
非公平模式:新到的家庭代表,直接去问看井人井是否空闲了
1. 空闲了就获取打水权
2. 不空闲,再看一眼有没有人在打水?
2.1 没人,再去问看井人
2.2 有人,是不是自家人?是则去打水,否则去排队
公平锁模式:新到的人(比如叫张三),先肉眼看是否有人在打水?
1. 没人的话再看前面有没有人排队
1.1 有人排队,乖乖的去排队
1.2 没人排队,去问看井人决定是否成功获取打水权
2. 有人在打水,问问打水人跟张三是否一家的?
2.1 是,可以来打水
2.2 否,去排队
参考资料: