前言
上个博客对ReentrantLock之非公平锁进行了剖析,下面我们通过源码对公平锁进行分析,并讲述它与非公平锁的区别
构造方法
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
这里给定参数true即可,创建公平锁的ReentrantLock的对象
源码分析
首先公平锁与非公平锁的实现就两个地方不一样,其他的地方都是一样的,下面我们将对其进行分析
先是lock方法
final void lock() {
acquire(1);
}
①这里仔细看,其实这里就已经和非公平锁不一样了,我们把之前的非公平锁的代码拿出来对比一下
final void lock() {
if (compareAndSetState(0, 1))//CAS操作,更新成1
//当前线程设置为持有锁的线程
setExclusiveOwnerThread(Thread.currentThread());
else
//锁已经被其他人获得
acquire(1);
}
可见非公平锁进来是直接通过CAS操作将状态改为1,强行占有资源,然后设置当前线程为持有锁的线程。而公平锁则通过acquire方法去获取。这里我们再对其方法进行解释,
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
acquire方法是独占式获取同步状态,如果当前线程获取同步状态成功,则由该方法返回,否者将会进入同步队列等待,该方法则会调用重写的tryAcquire方法
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
②就是tryAcquire这个方法和非公平锁不一样了,注意我标注***的位置,那里开始了不同
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;
}
再来看一下非公平锁这里的实现
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;//重入次数加1
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);//更新state
return true;
}
return false;
}
比较一下,唯一的不同在于判断条件多了hasQueuedPredecessors方法,即加入了同步队列中当前节点是否由前驱节点的判断,如果该方法返回true,则证明有线程比当前线程更早的获得锁,因此需要等待前驱线程获取并释放锁,才能继续获取锁。
然后看一下hasQueuedPredecessors方法的实现,实现了判断当前节点是否由前驱节点
ublic 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.其实公平锁和非公平锁的实现就两个不同一个是lock()方法一开始的实现,另外一个则是重写父类的tryAcquire方法的区别了,两个不同点都是,非公平锁都是想强行获取资源,而公平锁则比较好,排队。
2.那么非公平锁有什么缺点呢?显而易见,可能导致后面排队等待的线程等不到相应的cpu资源,从而引起线程饥饿。