本文共 4467 字,大约阅读时间需要 14 分钟。
首先关于AbstractQueuedSynchronizer就不过多介绍了,在之前的文章中已经详细的介绍过了先关部分,不熟悉的同学建议先看之前的文章中的内容。
ReentrantLock默认情况下是非公平锁,另外还提供了一个带boolean类型的构造方法,传入true,就表示要使用公平锁。
/** * 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(); }
从源码上看,两者实现方式的区别十分简单,只有两处不一样。
1、调用lock方法时,如果是非公平锁的方式,首先会先通过CAS尝试获取一次锁,如果获取失败,才会调用acquire方法,而公平锁则是直接就调用acquire方法。
非公平锁
final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
公平锁
final void lock() { acquire(1); }
2、tryAcquire方法,比较下两个方法,差别就是公平锁多了一个hasQueuedPredecessors方法。
非公平锁
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) { 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; }
公平锁
/** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. */ 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; }
hasQueuedPredecessors这个方法想达到的目的就是判断当前等待队列是否已经有其他线程在等待了,如果有则返回true,如果没有则返回false。
public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized // before tail and on head.next being accurate if the current // thread is first in queue. Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
如果hasQueuedPredecessors返回了true,那么当前线程就不会继续尝试获取锁了,而是直接把自己添加到队列的尾部,通过addWaiter方法。
如果不去关注hasQueuedPredecessors方法的细节,直接带着结果来理解,实现公平与非公平的方式就很简单,公平锁的思想无非就是,每个线程在尝试获取锁前先判断等待队列中是否已经有别的线程了,如果有则加入到队列尾部,等待前一个线程唤醒,而非公平锁则不一样,lock方法时就会尝试获取一次锁,如果失败,则在tryAcquire时又会尝试获取一次锁,如果再失败才会去等待队列中排队。
流程图
public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized // before tail and on head.next being accurate if the current // thread is first in queue. Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
结合同步队列的图来理解
1、h != t
当这个条件为false时,则表示当前队列中为空,或者只有一个节点,所以为false时直接返回,当前线程可以尝试获取锁。
2、((s = h.next) == null || s.thread != Thread.currentThread())
(s = h.next) == null,首先当这个条件为true时,后面条件不需要判断,结合第一个条件来看,如果要满足h.next为null,看起来是很矛盾的事情,第一个条件中确认了此时队列中肯定不只一个节点,那么头节点为什么会不存在下一个节点呢?这个场景实际上是某一个节点正在处于入队过程中,next还没来及赋值,但是队列中肯定是已经存在别的线程了,所以直接返回true,让当前线程排队等待。
如果h.next不为null,那么就可以肯定当前队列中一定存在等待的线程,而s.thread != Thread.currentThread()就是为了判断,next线程与当前线程是否是同一个,如果是同一个则返回false,表示轮到自己了,那么当前线程就可以尝试获取锁了,如果不是同一个则返回true,当前线程去队列中排队。
转载地址:http://eolrb.baihongyu.com/