什么是公平锁?什么是非公平锁?

公平锁:多个线程按照申请锁的先后顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能获得锁。

非公平锁:多个线程去获得锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。

优缺点比较

公平锁

优点:所有的线程都能够得到资源,不会饿死在队列中。

缺点:吞吐量会下降很多,队列里面除了第一个线程,其它线程都会阻塞,CPU唤醒阻塞线程的开销会很大。

非公平锁

优点:可以减少CPU唤醒线程的开销,整体的吞吐率提高。

缺点:可能导致队列中的线程一直获取不到锁或者长时间获取不到锁,导致饿死。

源码体现

ReentrantLock中就提供了公平锁和非公平锁两种模式(默认是非公平实现)。

公平锁实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
//只有当队列为空或自己是队列头的时候,才会尝试CAS修改state
//修改state成功后,才算获取到了锁,否则加入到队列中等待
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;
}
1
2
3
4
5
6
7
8
9
10
11
12
/*
判断当前队列是否为空或者自己是否是在同步队列的首位,
如果是,则返回true
否则返回false
*/
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}

公平锁只有在队列为空,或者自己为队头的时候才会尝试去修改state,当修改成功后则直接拿到了锁,否则进入排队。

非公平锁实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//state==0,通过CAS尝试修改state,而不是直接去排队
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;
}

非公平锁当state==0,就会立即去尝试CAS修改state,修改成功了则直接拿到了锁,否则加入阻塞队列。