鎖按照公平性劃分為公平鎖和非公平鎖,在Java中,ReentrantLock有這兩種鎖的具體實現(xiàn),下文進行展示叠纷。
說明
以食堂打飯的場景舉例說明
1. 公平鎖
想要獲取鎖資源的線程在隊列里進行排隊等待,新來的線程去隊尾排隊潦嘶,鎖資源釋放的時候只有隊首線程可以獲得鎖資源涩嚣。
- 排隊等待
2. 非公平鎖
新來的線程直接和隊首線程爭搶鎖資源,如果爭搶到了掂僵,則直接獲取鎖資源航厚,隊首線程繼續(xù)等待。
如果新來的線程競爭失敗锰蓬,則去隊尾進行排隊幔睬,只能等待隊列前所有線程執(zhí)行完畢后自己才能獲取鎖。
注意: 鎖的非公平性只在首次和隊首線程進行鎖競爭時有體現(xiàn)芹扭,競爭失敗入列后則與公平鎖執(zhí)行方式一致
- 插隊成功
- 插隊失敗
重點代碼
1. 初始化默認為非公平鎖麻顶,也可以設置為公平鎖
public ReentrantLock() {
// 默認為非公平鎖
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
// 可以設置為公平鎖
sync = fair ? new FairSync() : new NonfairSync();
}
2. 入列之前l(fā)ock方法中嘗試插隊獲取鎖
- 公平鎖排隊獲取鎖
static final class FairSync extends Sync {
final void lock() {
// 排隊獲取鎖,如果沒獲取到就排隊
acquire(1);
}
}
-
非公平鎖入列之前
首次
嘗試獲取鎖
static final class NonfairSync extends Sync {
final void lock() {
// 插隊嘗試獲取鎖(第一次嘗試)
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 排隊獲取鎖冯勉,如果沒獲取到就排隊
acquire(1);
}
}
3. tryAcquire時第二次嘗試插隊獲取鎖資源
- 公平鎖
static final class FairSync extends Sync {
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// hasQueuedPredecessors() 表示前面是隊列中是否有線程在排隊(true-有澈蚌、false-沒有)
// 這段的邏輯是隊列中沒有線程排隊了摹芙,才能獲取鎖
// 公平鎖就要保證新來的線程始終到隊尾排隊
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) {
// 相比公平鎖沒有hasQueuedPredecessors() 灼狰,說明不管隊列中有沒有排隊的線程,
// 只要能獲取到鎖資源浮禾,鎖資源就交給新來的線程交胚。
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;
}
4. 兩次都沒有插隊獲取到所資源份汗,則入列變成公平獲取鎖的一員
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
// acquireQueued() 入列等待
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
意義
ReentrantLock默認使用非公平鎖的意義是:
非公平鎖減少了線程上下文切換,犧牲了公平性但是提高了性能蝴簇。
提供公平鎖是對鎖的獲取順序進行了保證杯活,犧牲了部分性能。