ReentrantLock锁的实现是基于AQS实现的,所以先简单说下AQS:
AQS是AbstractQueuedSynchronizer缩写,顾名思义:抽象的队列同步器,它是JUC里面许多同步工具类实现的核心
其实简单来说AQS有两个核心,一个是volatile修饰的int类型state,这个是记录处于等待中需要持有锁和正在持有锁的线程数量
/** * The synchronization state. */ private volatile int state;
第二个就是Node内部类,他是AQS里面FIFO双向队列节点的实现,他的一些属性如下:
static final class Node {
volatile int waitStatus;//等待状态 volatile Node prev;//前驱节点 volatile Node next;//后继节点 volatile Thread thread;//申请同步状态的线程 Node nextWaiter;//等待节点的后继节点(后续出AQS详细篇再细讲) }
这种结构可以从任意的一个节点开始很方便的访问前驱和后继节点。每个Node由线程封装,当线程竞争失败后会加入到AQS队列中去。
基于这些再继续聊下ReentrantLock的公平和非公平锁的实现
ReentrantLock锁的实现基于AQS,如下sync抽象内部类:
abstract static class Sync extends AbstractQueuedSynchronizer{ abstract void lock(); }
Sync的子类有两个:FairSync和NonfairSync(公平和非公平锁的具体实现类),两个锁的不同就在于两个方法的实现不同lock():加锁的方法,还有tryAcquire(int acquires):尝试持有锁的方法,获取成功返回true,失败返回false
看看两把锁的源码实现:
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } 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; } }
static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } } nonfairTryAcquire方法是在父类Sync中定义: 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; }
可以看见,NonfairSync在使用lock()加锁的时候就已经体现了非公平性了,因为lock()加锁的时候直接尝试使用CAS获取锁,如果获取到了就不会入等待队列,所以会有后来的线程先抢占到锁;
那如果没有抢占到锁呢?会走else的acquire(1)方法。
先看看acquire方法的定义:
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
这里会发现acquire调用了tryAcquire方法,而在上面两把锁的源码中各自重写了tryAcquire方法
这个方法中当getState()获取同步状态为0(没有线程持有锁)时候,继续使用用CAS获取锁
两个重写的方法唯一不同之处在于,公平锁加了hasQueuedPredecessors()方法:
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()); }
这个方法主要是用来判断线程需不需要排队,因为队列是FIFO的,所以需要判断队列中有没有相关线程的节点已经在排队了。有则返回true表示线程需要排队,没有则返回false则表示线程无需排队。
而非公平锁就没有判断FIFO队列是否还有排队。
小结:
ReentrantLock中公平锁和非公平锁的实现主要有两处区别:
1.在lock()方法中,非公平锁一进来就尝试CAS获取锁,不会管等待队列里面是否有等待线程
2.在tryAcquire方法中,判断state等于0(没有线程持有锁的情况)后,公平锁会先调用hasQueuedPredecessors()方法判断FIFO队列是否有等待线程,没有才继续尝试获取锁,而非公平锁是直接CAS获取锁
值得注意的是众所周知ReentrantLock是可重入锁:
不管是公平锁还是非公平锁,他们重写的尝试抢占锁的tryAcquire方法里面都有这样一段代码
else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; }
这个是判断state不等于0(有线程持有锁或者等待线程时候)之后,继续判断当前持有锁的线程是不是等于当前申请锁的线程,如果等于,直接返回true并且state+1,所以当前线程不用等待继续持有锁;这就完成了可重入的特性!