前言
ReentrantLock是公平锁还是非公平锁?
ReentrantLock通过参数控制,可以是公平锁也可以是非公平锁。
公平锁和非公平锁
- 公平锁:指多个线程按照申请的顺序来读取锁,简单来说如果一个线程组里,能保证每个线程都能拿到锁。
- 非公平锁:获取锁的方式是锁机的,保证不了每个线程都能拿到锁。
ReentrantLock如何实现的
看一段源码
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);
}
}
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;
}
}
ReentrantLock 里面有一个内部类Sync
,Sync继承AQS,有公平锁FairSync
和非公平锁NonfairSync
两个子类,ReentrantLock默认使用非公平锁。
公平锁和非公平锁的lock()
方法唯一的区别就是多了一个限制条件hasQueuedPredecessors()
- 非公平锁会带来锁饥饿,什么是锁饥饿
当有几个线程同时来抢占锁时,其中有的线程是一直抢到锁,但一些线程由于优先级太低,一直得不到CPU调度执行,导致其他线程一直抢到不锁,这个就是出现了饥饿锁。
- ReentrantLock为什么要设计公平锁和非公平锁,他们有什么优缺点。
- 公平锁获取该锁和释放锁后的相关操作,相关线程也会从休眠和恢复之前变化,这个涉及用户态内核态互相转变。
- 非公平锁获取锁不会遵循先到先得得规则,没有了阻塞和恢复执行的步骤,避免了线程休眠和恢复的操作。
- 所以非公平锁性能高于公平锁,更能重复利用CPU的时间。
- 如何是为了各个线程都可以获取锁资源,则推荐采用公平锁,等待的线程不会饿死
- 如果为了业务有更大的吞吐量,则采用非公平锁。
在java中,锁的默认实现都是非公平锁,原因是非公平锁效率更高,非公平锁注重性能,而公平锁注重的是锁资源的平均分配。