其中有一些值的概念不太清楚,參考了:
https://blog.csdn.net/lsgqjh/article/details/63685058(這一位大佬,講的很細!!)
https://blog.csdn.net/mulinsen77/article/details/84583716
在此感謝!
Lock
Lock接口功能:
public interface Lock {
// 獲得鎖
void lock();
// 獲得鎖
void unlock();
// lock非阻塞版本,成功傳回true
boolean tryLock();
// 添加嘗試時間,時間到傳回false
boolean tryLock(long time, TimeUnit unit)
// 傳回一個螢幕對象
Condition newCondition();
}
AQS
在看ReenTrantLock之前,先大緻看看AQS都做了什麼:
這是一個簡化的線程隊列模型:
重要屬性:
-
state:代表了資源是否處于鎖定狀态;
1:鎖定(已經有線程拿鎖,如果重入了,此值一直累加)2:未鎖定
線程拿鎖,就是通過CAS修改state,修改成功,則拿到鎖;
-
Node内部類:
每一個Node裝載一個線程;對線程通過雙向連結清單的方式排隊;
-
Node内部類:還定義資源是 獨占 / 還是共享
也就是每個線程都有一個mode,辨別是獨占,還是共享;
Node EXCLUSIVE:代表獨占;
Node SHARED:代表共享;
先看幾個重要方法,後面會用到;
acquire
acquire
:顧名思義擷取,擷取鎖的方法
tryAcquire
:就先當作是獲得鎖,傳回true,沒拿到鎖,傳回false;這個方法是ReenTrantLock的方法,後面會講;
addWaiter
:如果沒拿到鎖,将目前要拿鎖的線程加入線程隊列
acquireQueued
:已經入隊完成,會進一步判斷,後面講;主要是判斷線程是否被挂起;
- true:挂起
- false:拿鎖成功了
是以:
當:拿鎖失敗,并且線程被挂起,就會執行
selfInterrupt();
,執行線程的中斷;
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
addWaiter
這個方法就是:(CAS操作入隊)
讓目前線程包裝成Node,
隊列存在,直接入隊
如果隊列不存在,調用
enq(node);
,初始化隊列,再入隊
(入隊:将node設定為隊列的tail尾部節點,并且與tail雙向關聯起來)
private Node addWaiter(Node mode) {
// 包裝線程為Node,并且是獨占的
Node node = new Node(Thread.currentThread(), mode);
// 拿到線程隊列的尾節點
Node pred = tail;
// 如果pred存在,即隊列非空
if (pred != null) {
node.prev = pred;
// CAS操作成功入隊,将Node設定為tail
if (compareAndSetTail(pred, node)) {
// 因為是雙向連結清單,要再鍊一次
pred.next = node;
return node;
}
}
// 隊列為空,調用enq,初始化隊列,并入隊
enq(node);
return node;
}
acquireQueued
此方法:是線程沒拿到鎖,入隊之後執行;
主要是:如果發現目前線程的前一個結點是隊列的head,那麼會再次嘗試拿鎖;
也就是說,此時線程隊列,就倆線程,一個是head,持鎖線程,一個是目前線程;
那麼目前線程會不斷嘗試拿鎖(
for (;;)
)
最終傳回的boolean型
interrupted
:
- true:挂起
- false:拿鎖成功了
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 拿到目前線程的前一任節點
final Node p = node.predecessor();
// 發現前任是head,再次嘗試拿鎖
if (p == head && tryAcquire(arg)) {
// 拿鎖成功,node設定為head
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 判斷是否将目前線程挂起
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
ReenTrantLock
實作Lock接口
ReenTrantLock隻有一個内部屬性:就是
Sync内部類
的鎖抽象對象
// 這是一個父類,兩個子類分别實作公平鎖,非公平鎖
private final Sync sync;
三個内部類:
- Sync(繼承AQS):鎖抽象;
- NonfairSync(繼承Sync):非公平鎖抽象;
- FairSync(繼承Sync):公平鎖抽象;
構造器
我們在建立ReenTrantLock對象,調用構造器時,就會建立不同的Sync(鎖抽象的實作)
// 非公平鎖
public ReentrantLock() {
sync = new NonfairSync();
}
// 公平鎖
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
公平鎖源碼(拿鎖,排隊,重入鎖)
當我們調用了
lock.lock();
公平鎖下,sync已經是
FairSync
的執行個體了;
調用
sync.lock()
public void lock() {
sync.lock();
}
然後調用FairSync内部類下的lock方法:(建議點進源碼,看下)
acquire(1)
:此方法是AQS下的方法;(去上面看!)在内部是調用了下面的tryAcquire方法;
這個參數1是幹嘛的:就代表嘗試擷取鎖;之前AQS的屬性state,如果為0表示未鎖定;
這個1就是要通過
compareAndSetState(0, acquires)
CAS操作進行加鎖的;
(這裡也是通過記憶體位址stateOffset,拿到state的狀态,CAS操作不再贅述)
嘗試将state設定為1,即拿到鎖;
重點:tryAcquire方法(實作了拿鎖,排隊,重入鎖)
static final class FairSync extends Sync {
final void lock() {
acquire(1); // 調用AQS acquire方法,前面講了
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 從AQS中拿到目前資源的state狀态
int c = getState();
// 如果為0,則表示未鎖定,可以嘗試擷取鎖
if (c == 0) {
// hasQueuedPredecessors是看目前線程隊列中是否有其他線程(非公平鎖沒有此判斷)
// 如果有其他線程,目前線程不允許拿鎖,而是去排隊
// 如果沒有線程,并且CAS操作将state置1,那麼目前線程就拿到了鎖
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
// 設定獨占的資源持有者為目前線程,即拿鎖,并傳回true
setExclusiveOwnerThread(current);
return true;
}
}
// state非0,即資源已被鎖定
// 判斷目前的線程,是不是占用鎖的線程
// 是,則累加state,也就是重入鎖的實作
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
// 疊加state狀态
setState(nextc);
return true;
}
return false;
}
}
非公平鎖源碼
同樣是lock()方法,不再贅述,隻不過這裡的
Sync
執行個體,是
NonfairSync
的執行個體;
與公平鎖的主要差別:線程是否排隊
直接看NonfairSync
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
// 加鎖方法
final void lock() {
// CAS嘗試加鎖
if (compareAndSetState(0, 1))
// 成功,設定資源獨占者為目前線程
setExclusiveOwnerThread(Thread.currentThread());
else
// 底層依然調用下面的tryAcquire
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
nonfairTryAcquire
方法是其父類
Sync
下的方法
類似于公平鎖的
tryAcquire
方法
差別是:不再進行
hasQueuedPredecessors()
方法的判斷,直接嘗試擷取鎖
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)
throw new Error("Maximum lock count exceeded");
// 疊加state狀态
setState(nextc);
return true;
}
return false;
}