ReentrantLock源碼分析

首先,想先梳理一下自己看源碼的目的,最近有篇文章特別火《程序員12小時驚魂記:凌晨遷移數據出大事故!》,裏面強調了解決問題的能力很重要,這給我一種只看源碼和設計模式關鍵時刻不給力的感覺,週末去看了魯能和恆大的比賽,那種在瞬間做出決策的壓力只會比事故現場更緊張,而且不能有一點猶豫。我想競技體育的魅力,就在於此吧,要把所有的技能都訓練到潛意識層面,纔會在瞬間爆發出來。所以,要不斷的研究不斷的實踐,弄懂原理,融匯貫通。

看源碼步驟:
1、選中類名右擊,選中Giaggrams->Show Diagram查看類關係圖。
2、在類關係圖中右擊,選中Show Categories->Methods等查看類屬性和方法等。可以看到ReentrantLock實現了Lock,Serializable兩個接口,有Sync、NonfairSync和FairSync三個內部類,NonfairSync和FairSync繼承Sync類,Sync繼承和ReentrantLock同一個包下的AbstractQueuedSynchronizer抽象類。
3、在自己的測試工程中寫一個測試類,創建ReentrantLock對象,調用其中的方法。

public class ReentrantLockTest {
	public static void main(String[] args){
		ReentrantLock reentrantLock = new ReentrantLock();
		reentrantLock.lock();
		reentrantLock.unlock();
	}
}

(1)從構造函數開始看,無參構造方法中只是實現了sync = new NonfairSync(),即默認使用非公平鎖。
(2)常用的方法lock(),實現很簡單,只是調用了sync.lock(),抽象方法,由子類實現。查看默認的非公平鎖的實現。

final void lock(){
	if (compareAndSetState(0,1)){
		setExclusiveOwnerThread(Thread.currentThread());
	} else{
		acquire(1);
	}
}

public final void acquire(int arg){
	if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE),arg)){
		selfInterrupt();
	}
}
  1. setExclusiveOwnerThread把當前線程設置成持有鎖的線程。
  2. 如果CAS失敗,說明鎖已經被別人獲取,那麼通過acquire()嘗試去獲取鎖。
  3. 其中的抽象類的非抽象方法,AbstractQueuedSynchronizer.java中tryAcquire方法,在抽象類中只是簡單了拋出了異常,這種主要是由子類去實現,要去看不同的子類都是怎麼實現的該方法。這個方法中,由FairSync公平鎖和NonFairSync非公平鎖的來實現此方法。
  4. 在NonFairSync非公平鎖中調用nonfairTryAcquire(acquires);
final boolean nonfairTryAcquire(int acquires){
	final Thread current = Thread.currentThread();
	int c = getStatus();
	if (c == 0){
		if (compareAndSetState(0,1)){
			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;
}
  • getStatus()獲取的狀態,就是上面CAS設置的狀態,如果狀態(c ==
    0)說明當前沒有任何線程獲取到鎖,那就再獲取一遍鎖,設置線程,返回true;
  • current == getExclusiveOwnerThread()說明當前線程重入
  • setState(),把重入次數+1更新到status,返回true說明獲取到這個鎖。
  1. 繼續看acquire()方法中的判斷條件,addWaiter(Node.EXCLUSIVE)中 EXCLUSIVE定義 = null; 註釋中表明,此標記表示節點在獨佔模式下等待。
private Node addWaiter(Node mode){
	Node node = new Node(Thread.currentThread(),mode);
	Node pred = tail;
	if (pred != null){
		node.prev = pred;
		if (compareAndSetTail(pred,node)){
			pred.next = node;
			return node;
		}
	}
	enq(node);
	return node;
}

private Node enq(final Node node){
	for (;;){ // me:原來必須初始化是這樣寫
		Node t = tail;
		if (t == null){ //Must initialize
			if (compareAndSetHead(new Node())){
				tail = head;
			}
		} else {
			node.prev = t;
			if (compareAndSetTail(t,node)){
				t.next = node;
				return t;
			}
		}
	}
}
public abstract class AbstractQueuedSynchronizer 
	extends AbstractOwnableSynchronizer 
	implements java.io.Serializable {
	
	static final class Node{
		...
		volatile Node prev;
		volatile Node next;
		volatile Thread thread;
		Node nextWaiter;
		Node(Thread thread, Node mode){
			this.nextWaiter = mode;
			this.thread = thread;
		}
		...
	}
}
  • pred != null,說明當前鏈表不爲空,則把node追加到鏈表的尾部,並返回node。
  • addWaiter()方法保證把Node放到一個鏈表裏去,並返回尾節點。
  1. 繼續看acquire()方法中的判斷條件,acquireQueued(addWaiter(Node.EXCLUSIVE),arg))
final boolean acquireQueued(final Node node, int arg){
	boolean failed = true;
	try {
		boolean interrupted = false;
		for (;;){
			final Node p = node.predecessor();
			if (p == head && tryAcquire(arg)){
				setHead(node);
				p.next = null; //help GC
				failed = false;
				return interrupted;
			}
			if (shouldParkAfterFailedAcquire(p,node) && 
				parkAndCheckInterrupt()){
				interrupted = true;
			}
		}
	} finally {
		if (failed)
			cancleAcquire(node);
	}
}
  • p == head是鏈表的特性,head節點不存數據,所以如果當前節點的前驅節點爲head時,則移除掉原head,把當前node節點設置爲head。
  • shouldParkAfterFailedAcquire()裏面有信號量,第一次執行結果爲false,且設置信號量,繼續執行for的死循環,使用信號量,將線程掛起,然後等待一直線程被喚起。
  • finally表示如果執行過程中有異常情況,則取消當前線程獲取鎖的動作。
  • for循環,從傳入的尾節點開始,不斷的獲取前驅節點,只有在獲取到head節點時,才能走到唯一的出口
  1. 繼續看acquire(),如果獲取鎖失敗,且發生線程中斷,則中斷當前線程。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章