ReentrantLock
是一个可重入的独占锁,内部使用AQS
实现。state
记录着持有锁的线程的进入同步代码块的次数。
Sync
是一个抽象类,他没有重写tryAcquire
方法,但是他多了一个nonfairTryAcquire
方法,该方法是一个非公平获取锁资源的方法。提供了一个抽象的lock
方法,这是一个统一的加锁方法。
abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L; /** * Performs {@link Lock#lock}. The main reason for subclassing * is to allow fast path for nonfair version. */ abstract void lock(); /** * Performs non-fair tryLock. tryAcquire is implemented in * subclasses, but both need nonfair try for trylock method. */ 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; } 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; } protected final boolean isHeldExclusively() { // While we must in general read state before owner, // we don't need to do so to check if current thread is owner return getExclusiveOwnerThread() == Thread.currentThread(); } final ConditionObject newCondition() { return new ConditionObject(); } // Methods relayed from outer class final Thread getOwner() { return getState() == 0 ? null : getExclusiveOwnerThread(); } final int getHoldCount() { return isHeldExclusively() ? getState() : 0; } final boolean isLocked() { return getState() != 0; } /** * Reconstitutes the instance from a stream (that is, deserializes it). */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); setState(0); // reset to unlocked state } }
因为在ReentrantLock
是支持公平锁和非公平锁的,这里的nonfairTryAcquire
方法就是非公平获取锁资源的实现。我们可以看到分两种情况,一是当没有线程持有锁时,直接cas修改state
,修改成功就获取到了锁,并将当前持有独占锁的线程设置为自己。二是判断当前自己是不是持有锁的线程(可能是重入),如果是就只需增加锁的重入次数。最后就是有其他线程占用了锁,返回false。
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; }
进行锁资源的释放,该方法是线程安全的,因为独占模式只有一个线程只有锁,也就只有一个线程可以释放锁资源。释放锁刚好更获取锁资源反着来,减少锁的重入次数,设置当前持有锁的线程为null,当state
为0时,也就说明当前线程释放了锁资源,就返回true,从而唤醒后继节点。
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; }
可以通过有参构造方法,指定使用公平锁还是非公平锁,默认无参构造方法是非公平锁。
public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
实现了上面我们说的Sync
类型,补上对应两个需要实现的方法lock
和tryAcquire
,这里的tryAcquire
直接调用我们上面说的非公平获取锁资源的方法nonfairTryAcquire
。这里lock
方法本来可以直接调用acquire
方法,但是他在前面还是做了一次快速获取锁资源的尝试,直接casstate
,如果成功就设置持有锁的线程是当前线程,如果失败就再走一次正常的逻辑。
非公平性:代码我们可以看到,主要线程获取到了锁资源,就表示该线程持有了锁,不关系队列中释放有排在其前面的线程在等待。优点很明显,就是吞吐高;缺点也很明显,会导致线程饥渴,一直等待无法获取到锁资源(因为会被其他线程抢)。
static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }
也是实现了lock
和tryAcquire
方法,这里的lock
方法就有了一点变化了,没有在lock
里面之前获取锁资源了,而是全部交给了acquire
来处理,这体现了公平性的一点,因为如果直接跟上面的非公平锁中lock
一样获取锁资源的话,就没办法办证公平性了。tryAcquire
中相比非公平锁,多了hasQueuedPredecessors
方法的判断,该方法用来判断当前节点是否有前驱节点,如果有,就获取锁失败,也就是说来获取锁资源的线程必须保证他没有前驱节点,其他代码基本和非公平锁一样。
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } /** * 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; } } public final boolean hasQueuedPredecessors() { 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()); }
实现了统一的lock
接口,我们在使用时,一般是使用lock
接口中的方法进行加锁和解锁。这里的sync
就是构造方法中创建的NonfairSync
或FairSync
,另外newCondition
是创建一个条件队列对象。
public class ReentrantLock implements Lock, java.io.Serializable { ... public void lock() { sync.lock(); } public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } public boolean tryLock() { return sync.nonfairTryAcquire(1); } public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); } public void unlock() { sync.release(1); } public Condition newCondition() { return sync.newCondition(); } }
ReentrantLock
其实很简单,如果你看过AQS
的源码分析,那ReentrantLock
就是基于AQS
实现一种独占(排他)可重入的锁。他提供了公平和非公平模式,公平性是在获取锁资源时,检查当前节点是否有前驱节点。最后是实现了标准的Lock
接口,方便使用统一的lock
和unlock
进行加锁和解锁。