
死神--->一番隊隊長山本元柳齋重國
目錄:
- 1、Lock介紹
- 2、Lock的實現(xiàn)類ReentrantLock
- 3、AQS簡介
- 4、ReentrantLock類體系
- 5 、Lock使用
- 6、AQS原理:
- 6.1、AQS內(nèi)存模型
- 6.2、AQS類體系
- 6.3、AQS模板方法
- 6.4、Node節(jié)點
- 6.5、CHL同步隊列
- 6.6、同步狀態(tài)獲?。╝cquire)與釋放(release)
- 6.7、線程阻塞與喚醒
- 7、AQS-產(chǎn)品之一ReentrantLock
- 7.1、圖解:獲取鎖、釋放鎖、入同步隊列
- 7.2、公平鎖&非公平鎖
- 8、面試常見問題
- 1、公平鎖(FairSync)和非公平鎖(NonfairSync)的區(qū)別?
- 2、ReentrantLock如何處理線程中斷的?
- 3、未完待續(xù)~
1、Lock介紹
Lock 接口實現(xiàn)類提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作。此實現(xiàn)允許更靈活的結(jié)構(gòu),可以具有差別很大的屬性,可以支持多個相關(guān)的 Condition 對象(Condition實現(xiàn)類ConditonObject來實現(xiàn)線程的通知/與喚醒機(jī)制,關(guān)于Condition傳送門
)。
2、 Lock的實現(xiàn)類ReentrantLock
ReentrantLock是根據(jù)AQS實現(xiàn)的獨占鎖(exclusive)。
3、 AQS簡介
AQS全稱AbstractQueuedSynchronizer,是java并發(fā)包中的一個類,該類更像是一個框架(模板方法模式),提供了一些模板方法供子類實現(xiàn),從而實現(xiàn)了不同的同步器,如下圖所示。ReentrantLock,ReentrantReadWriteLock,CountDownLatch、ThreadPoolExecutor等 這些常見類都使用了AQS。
4、 ReentrantLock類體系

ReentrantLock類體系結(jié)構(gòu)
5、Lock使用
eg:加鎖/解鎖偽代碼
Lock lock = new xxxLock();
try{
// 加鎖
lock.lock();
balabala作相應(yīng)業(yè)務(wù)
} finally{
// 解鎖
lock.unlock();
}
6、AQS原理:
6.1、AQS內(nèi)存模型

AQS內(nèi)存模型
6.2、AQS類體系

AQS類體系結(jié)構(gòu)
6.3、AQS模板方法

模板方法

抽象方法
6.4、Node節(jié)點

Node節(jié)點
6.5、CHL同步隊列

CHL同步隊列
6.6、同步狀態(tài)獲?。╝cquire)與釋放(release)

獲取&釋放同步狀態(tài)
6.7、線程阻塞與喚醒

線程掛起&喚醒
7、AQS-產(chǎn)品之一ReentrantLock:
7.1、圖解:獲取鎖、釋放鎖、入同步隊列
背景:Thread-A 、Thread-B、Thread-C順序獲取鎖,模擬內(nèi)存中同步隊列變化。還要一種特殊情況:Thread-D獲取鎖失敗,同時入同步隊列失敗(最倒霉的線程D)。

Thread-A獲得重入鎖

Thread-B獲得鎖失敗,入同步隊列

Thread-C入同步隊列

最倒霉的Thread-D加鎖失敗、入隊失敗g

如果node!=tail,不是尾節(jié)點,尾結(jié)點是Thread-X

走uparkSuccessor(node)

假設(shè)內(nèi)存中Thread-N中的 waitStatus=1
分析下第7-3步驟為何要倒序循環(huán)喚醒節(jié)點?

為何倒序喚醒節(jié)點?
7.2、公平鎖&非公平鎖


thread-6公平鎖入隊

thread-6非公平鎖入隊
8、面試常見問題:
- 1、公平鎖(FairSync)和非公平鎖(NonfairSync)的區(qū)別?
鎖的公平性:獲取鎖順序而言的。
共同點:都是繼承自ReentrantLock內(nèi)部類Sync。
差異:- 公平鎖,那么鎖的獲取順序就應(yīng)該符合請求的絕對時間順序,也就是 FIFO。
- 非公平鎖,在獲取鎖的時候,會先通過 CAS 進(jìn)行搶占。
另外公平鎖判斷條件多了hasQueuedPredecessors()方法,也就是加入了[同步隊列中當(dāng)前節(jié)點是否有前驅(qū)節(jié)點]的判斷,如果該方法返回 true,則表示有線程比當(dāng)前線程更早地請求獲取鎖, 因此需要等待前驅(qū)線程獲取并釋放鎖之后才能繼續(xù)獲取鎖。
公平鎖加鎖方法:
// 公平鎖-獲取鎖的方法
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 同步隊列中當(dāng)前節(jié)點是否有前驅(qū)節(jié)點
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 void lock() {
// 嘗試CAS加鎖
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 第一次嘗試失敗后,走父類通用加鎖邏輯
acquire(1);
}
// 父類模板方法回調(diào)tryAcquire方法
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;
}
- 2、ReentrantLock如何處理線程中斷的?
阻塞隊列使用的LockSupport.park();
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
因為LockSupport.park(),無法響應(yīng)Thread.interrupt(); 所以當(dāng)unpark()后使用Thread.interrupted()來判斷線程是否有中斷過。如果中斷過整個喚醒的線程在外層方法會繼續(xù)執(zhí)行一次中斷,詳情源碼如下:

image.png

image.png
static void selfInterrupt() {
Thread.currentThread().interrupt();
}