1.本文不講概念,用jdk8調試一步一步搞清AQS這個東西,請耐心並跟着本文實操。
現在我們先實現下面這個類:
public class MutexLock {
static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 3601133938511592218L;
/***
* 嘗試獲取鎖
*/
protected boolean tryAcquire(int arg) {
// 當 state 值爲0時,然後就改爲1,否則就返回false也不修改state值
if (compareAndSetState(0, 1)) {
// 設置當前線程爲 獨佔線程
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
//嘗試釋放鎖
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0) {
// state 爲 0 說明當前同步塊中沒有鎖了,無需釋放
throw new IllegalMonitorStateException();
}
//當前獨佔線程 設置爲null
setExclusiveOwnerThread(null);
// 將狀態變量的值設爲 0,以便其他線程可以成功修改狀態變量從而獲得鎖
setState(0);
return true;
}
// 當前線程是否被獨佔
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
}
// 將操作代理到 Sync 上
private final Sync sync = new Sync();
public void lock() {
sync.acquire(1);
}
/**
* 試圖釋放此鎖
*/
public void unlock() {
sync.release(1);
}
上面代碼仿照了ReentrantLock,實現了lock和unlock方法,實際操作都是Sync這個內部類操作的。
下面我們來測試下:
public static void main(String[] args) throws InterruptedException {
// final ReentrantLock lock = new ReentrantLock();
final MutexLock lock = new MutexLock();
/// 代碼A
lock.lock();
new Thread() {
public void run() {
//代碼B
lock.lock();
System.err.println("==============");
lock.unlock();
};
}.start();
Scanner sc = new Scanner(System.in);
System.out.print("Please enter a string : ");
String lein = sc.next();
if (lein.equals("1")) {
//代碼C
lock.unlock();
}
}
上面代碼大致解釋:
- 主線程加鎖,然後子線程lock時拿不到鎖會阻塞等待
- 主線程在控制檯輸入1 回車時,主線程unlock,子線程被喚醒直到執行完畢!
調試分析
1.代碼A 塊的執行
我們調用代碼A 【lock.lock();】 方法,跟蹤進到AQS類的acquire方法
執行子類的tryAcquire方法, 調試我們返回true。
返回上層函數,直接返回,到此main函數的代碼A 【lock.lock();】 方法已經執行完畢!
執行完總結:
AQS的兩個成員變量
state 的值 變成1。
exclusiveOwnerThread的值變成 當前main函數線程的引用。
2.代碼B塊的執行
同樣跟蹤到下面tryAcquire方法,整個方法返回false
回到上層方法,進入addWaiter
addWaiter方法如下
執行enq方法之前我們觀察Node的數據結構和值如下(記住)
進入enq方法,進入第一次循環。
第二次序號循環
到此我們能想象出如下數據結構圖
到此我們有了Node1節點和Node2節點
繼續走代碼
返回上一層,執行acquireQueued函數
acquireQueued函數的第一次循環
下面我們看shouldParkAfterFailedAcquire這個方法
上面執行完後,需要知道Node1節點的waitStatus變量變成-1了
第二次循環,上面shouldParkAfterFailedAcquire返回false,所以直接進入第二次循環
第二次進入shouldParkAfterFailedAcquire方法
返回上一層
進入parkAndCheckInterrupt方法
代碼B塊的執行總結
1.阻塞了當前子線程,利用LockSupport.park工具阻塞
2.在AQS內部構造瞭如下鏈表數據結構
今天就講到這裏吧,分析不清楚的敬請諒解。可以加我qq一起討論。
老生常談:深圳有愛好音樂的會打鼓(吉他,鍵盤,貝斯等)的程序員和其它職業可以一起交流加入我們樂隊一起嗨。我的QQ:657455400 表演視頻實例https://v.qq.com/x/page/f0517awx0x4.html