ReentrantLock是lock介面的一個實現類,裡面實現了可重入鎖和公平鎖非公平鎖
ReentrantLock公平鎖和不公平鎖實現原理
公平鎖會獲取鎖時會判斷阻塞佇列裡是否有執行緒再等待,若有獲取鎖就會失敗,並且會加入阻塞佇列
非公平鎖獲取鎖時不會判斷阻塞佇列是否有執行緒再等待,所以對於已經在等待的執行緒來說是不公平的,但如果是因為其它原因沒有競爭到鎖,它也會加入阻塞佇列
進入阻塞佇列的執行緒,競爭鎖時都是公平的,應為佇列為先進先出(FIFO)
預設實現的是非公平鎖
public ReentrantLock() {
//非公平鎖
sync = new NonfairSync();
}
還提供了另外一種方式,可傳入一個boolean值,true時為公平鎖,false時為非公平鎖
//公平鎖
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
非公平鎖
非公平鎖獲取鎖nonfairTryAcquire
方法,對鎖狀態進行了判斷,並沒有把鎖加入同步佇列中
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
//比較當前狀態 如果持有者是當前執行緒
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//如果不是 嘗試獲取鎖
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
//獲取鎖
return nonfairTryAcquire(acquires);
}
}
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;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
公平鎖
程式碼和nonfairTryAcquire
唯一的不同在於增加了hasQueuedPredecessors
方法的判斷
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
protected final boolean tryAcquire(int acquires) {
//獲取當前執行緒
final Thread current = Thread.currentThread();
int c = getState();
//判斷當前物件是否被持有
if (c == 0) {
//如果等待佇列為空 並且使用CAS獲取鎖成功 否則返回false然後從佇列中獲取節點
if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {
//把當前執行緒持有
setExclusiveOwnerThread(current);
return true;
}
}
//若被持有 判斷鎖是否是當前執行緒 可重入鎖的關鍵程式碼
else if (current == getExclusiveOwnerThread()) {
//計數加1 返回
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//不是當前執行緒持有 執行
return false;
}
}
acquire()獲取鎖
public final void acquire(int arg) {
//如果當前執行緒嘗試獲取鎖失敗並且 加入把當前執行緒加入了等待佇列
if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//先中斷當前執行緒
selfInterrupt();
}
關鍵程式碼
就是tryAcquire
方法中hasQueuedPredecessors
判斷佇列是否有其他節點
public final boolean hasQueuedPredecessors() {
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());
}
可重入性實現原理
線上程獲取鎖的時候,如果已經獲取鎖的執行緒是當前執行緒的話則直接再次獲取成功
由於鎖會被獲取n次,那麼只有鎖在被釋放同樣的n次之後,該鎖才算是完全釋放成功
1、獲取鎖方法
protected final boolean tryAcquire(int acquires) {
//獲取當前執行緒
final Thread current = Thread.currentThread();
int c = getState();
//判斷當前物件是否被持有
if (c == 0) {
//...略
}
//若被持有 判斷鎖是否是當前執行緒 可重入鎖的關鍵程式碼
else if (current == getExclusiveOwnerThread()) {
//計數加1 返回
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//不是當前執行緒持有 執行
return false;
}
每次如果獲取到的都是當前執行緒這裡都會計數加1
釋放鎖
protected final boolean tryRelease(int releases) {
//每次釋放都減1
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//等於0才釋放鎖成功
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
如果鎖被獲取n次,也要釋放了n次,只有完全釋放才會返回false