ReentrantLock类与Synchronized关键字的主要区别之一就是可以实现公平锁和非公平锁。我们看下ReentrantLock类是如果实现公平锁与非公平锁的。
1、非公平锁的实现
ReentrantLock默认构造函数为非公平锁(为什么?因为非公平锁的实现可以减少线程的切换,提高执行效率。)
public ReentrantLock() {
sync = new NonfairSync();
}
ReentrantLock中含有一个内部类NonfairSync,如下
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* 非公平lock方法。首先,直接调用AQS的compareAndSetState方法,原子设置同步状态的值。
* True,表明获取锁成功,设置AQS的成员变量独占锁的拥有者exclusiveOwnerThread为当前
* 线程
* False,获取锁失败。调用AQS的acquire方法。
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
//实现父类的tryAcquire方法。尝试以独占模式获取同步状态。
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
ReentrantLock在调用lock方法时,其实调用的是NonfairSync 类的lock方法,此方法先调用AQS的模板方法compareAndSetState来原子的设定同步状态的值,如果成功,则表明获取锁成功,失败,则调用AQS的模板方法acquire()。调用AQS的模板方法acquire()如下
//该方法调用NonfairSync类中重写的tryAcquire方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();// 如果没有获取到同步状态,并且最终线程的中断状态为true,则会调用selfInterrupt方法进行线程中断。
}
acquire方法是AQS实现的模板方法。tryAcquire方法需要具体的子类进行实现,这个方法一会再看。先看两个方法addWaiter()和acquireQueued() 此两个方法均为AQS的模板方法,分别如下
//addWaiter方法是AQS的模板方法,实现创建节点并进入同步队列。
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
//acquireQueued是AQS的模板方法,当前线程会根据公平原则来进行阻塞等待,直到获取到同步状态位置。并且会返回在阻塞过程中线程是否被中断过。
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; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
现在来看AQS的子孙类NonfairSync实现的tryAcquire方法
//实现父类的tryAcquire方法。尝试以独占模式获取同步状态。
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
nonfairTryAcquire()方法在AQS的子类Sync类中已实现,如下。(为什么放到Sync类中?)
//Sync实现的nonfairTryAcquire方法。
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {//同步状态为0,则再次设置同步状态,成功,则返回True
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {//同步状态不为0,当占用锁的线程为当前线程时,重入,返回成功。
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
2、公平锁的实现
公平锁调用Lock方法时,并没有上来调用compareAndSetState方法(因为不知道同步队列中是否有线程等待)。首先调用AQS的模板方法acquire方法,addWaiter方法与acquireQueued调用的逻辑相同,主要看公平锁版本tryAcquire方法的实现。
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
//注意这里与公平锁实现的不同,公平锁不能直接调用CAS方法,因为公平锁需要确认同步队列中是否有等待获取同步状态的线程
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;
}
}
我们发现与非公平锁相比,主要多一句!hasQueuedPredecessors,这是实现公平锁和非公平锁的关键。hasQueuedPredecessors方法是查询同步队列中是否有等待获取锁的线程,只有返回false时,才会设置同步状态的值。否则,加入同步队列阻塞等待获取锁。提现公平锁的特点。
参考文章:
(2)《Java并发编程的艺术》第五章Java中的锁。