1. 程式人生 > 其它 >ReentrantLock原始碼解析

ReentrantLock原始碼解析

簡介

  • ReentrantLock是AQS下一種實現,提供了公平鎖與非公平鎖兩種機制。
  • 預設為非公平鎖,可在ReentrantLock例項化時指定。
  • 公平鎖與非公平鎖區別在於,當一個執行緒獲取鎖時,如果當前鎖已被其他執行緒獲取。公平鎖直接將當前執行緒加入到阻塞佇列,而非公平鎖會嘗試再次獲取鎖,若獲取成功將不再阻塞。

初始化

public ReentrantLock() {
	sync = new NonfairSync(); // 非公平鎖
}

public ReentrantLock(boolean fair) {
	sync = fair ? new FairSync() : new NonfairSync(); // 指定公平|非公平鎖
}

獲取鎖

ReentrantLock提供了兩種獲取鎖的方式,lock()、tryLock()。
lock()獲取鎖時,如果鎖已被其他執行緒持有,當前執行緒會進入阻塞狀態。
tryLock()獲取鎖時,如果鎖已被其他執行緒持有,將放棄鎖的獲取,而是繼續執行當前執行緒。

public boolean tryLock()

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;
	}
	// 未獲取到鎖,不再阻塞等待,直接返回false結束鎖獲取
	return false;
}

final void lock()【NonfairSync】

final void lock() {
	// 如果當前鎖狀態為0,則將鎖狀態該為1,表示當前執行緒獲取當了鎖
	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();
}
// 非公平鎖嘗試獲取鎖
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;
}

執行至此處時,表示當前執行緒未獲取到鎖。

// 添加當前執行緒到等待佇列
private Node addWaiter(Node mode) {
	// 建立一個thread為當前執行緒的節點
	Node node = new Node(Thread.currentThread(), mode);
	// Try the fast path of enq; backup to full enq on failure
	Node pred = tail;
	if (pred != null) { // 當tail節點不為null時
		node.prev = pred; // 將當前節點的prev節點指向tail節點
		if (compareAndSetTail(pred, node)) { // 將tail節點指向當前節點
			pred.next = node; // 原tail節點的next節點指向當前節點
			return node;
		}
	}
	enq(node); // 當tail節點為空或者將tail節點指向當前節點失敗時,會執行到當前
	return node;
}
// 當前執行緒入隊
private Node enq(final Node node) {
	// 由於當前操作是非執行緒安全的,使用了CAS+自旋的方式來保證操作的原子性以及節點入隊
	for (;;) {
		Node t = tail;
		if (t == null) { // Must initialize
			if (compareAndSetHead(new Node())) // tail節點為空時,會建立一個thread為null節點,並使head、tail節點指向該節點
				tail = head;
		} else {
			// 此處與addWaiter()方法邏輯是一致的,將當前節點的prev節點指向tail節點,並將tail節點指向當前節點以及原tail節點的next節點指向當前節點
			// 如果將tail節點指向當前節點失敗,由於使用了自旋方式,會不斷嘗試該操作,直至將tail節點指向當前節點為止
			node.prev = t;
			if (compareAndSetTail(t, node)) {
				t.next = node;
				return t;
			}
		}
	}
}
// 此時當前執行緒節點以及加入到佇列中
final boolean acquireQueued(final Node node, int arg) {
	boolean failed = true;
	try {
		boolean interrupted = false;
		// 進入到此後,首先會嘗試獲取鎖,
		for (;;) {
			final Node p = node.predecessor(); // 獲取當前節點的prev節點
			if (p == head && tryAcquire(arg)) { // 如果prev節點是head節點,會嘗試獲取鎖
				// 此時表示當前執行緒獲取到了鎖,但節點仍在佇列中
				// 將當前節點設定為head節點
				// 很巧妙隱形的將當前節點從佇列中移除【因為此時已獲得鎖】
				setHead(node);
				p.next = null; // help GC
				failed = false;
				return interrupted;
			}
			// 將head節點waitStatus設為SIGNAL,並讓當前執行緒掛起,直至執行緒被喚醒,
			// 執行緒被喚醒後會再次嘗試獲取鎖,如果獲取失敗會再次被掛起,如此自旋直至獲取鎖為止
			if (shouldParkAfterFailedAcquire(p, node) &&
				parkAndCheckInterrupt())
				interrupted = true;
		}
	} finally {
		if (failed)
			cancelAcquire(node);
	}
}
// 將當前節點設定為head節點
// 將節點的prev節點與thread設定為null即可
private void setHead(Node node) {
	head = node;
	node.thread = null;
	node.prev = null;
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
	int ws = pred.waitStatus; // 獲取prev節點waitStatus
	if (ws == Node.SIGNAL)
		return true;
	if (ws > 0) { // waitStatus>0,標段已取消
		// 從佇列中刪除當前節點的prev節點已取消的節點,但是否被GC回收來呢???是否原來的prev、next指向依然在。
		do {
			node.prev = pred = pred.prev; // 將當前節點的prev節點指向prev節點的prev節點
		} while (pred.waitStatus > 0);
		pred.next = node; // 當前節點新的prev節點指向當前節點
	} else {
		compareAndSetWaitStatus(pred, ws, Node.SIGNAL); // 設定當前節點的prev節點的狀態為SIGNAL
	}
	return false;
}
// 此時當前執行緒經過了層層疊嶂,依然未獲取到鎖,於是將當前執行緒掛起。
private final boolean parkAndCheckInterrupt() {
	LockSupport.park(this);
	return Thread.interrupted();
}

此時,lock()獲取指向完畢,如果獲取到了鎖就繼續執行當前執行緒,否則當前執行緒已掛起。

釋放鎖

final void lock()【NonfairSync】

public final boolean release(int arg) {
	if (tryRelease(arg)) { // 鎖是否成功
		Node h = head;
		if (h != null && h.waitStatus != 0) // head節點不為null並且狀態不為0時,喚醒一個阻塞中執行緒
			unparkSuccessor(h);
		return true;
	}
	return false;
}
// 嘗試釋放
protected final boolean tryRelease(int releases) {
	int c = getState() - releases;
	if (Thread.currentThread() != getExclusiveOwnerThread())
		throw new IllegalMonitorStateException();
	boolean free = false;
	if (c == 0) { // 鎖已釋放
		free = true;
		setExclusiveOwnerThread(null); // 釋放當前執行緒持有該鎖
	}
	setState(c); // 設定鎖狀態
	return free;
}
// 喚醒執行緒,node=head,但似乎沒有從佇列中刪除,是在喚醒後嘗試獲得鎖成功後刪除的
private void unparkSuccessor(Node node) {
	int ws = node.waitStatus; // waitStatus=SIGNAL,在shouldParkAfterFailedAcquire()中設定的
	if (ws < 0)
		compareAndSetWaitStatus(node, ws, 0); // 設定head的waitStatus為0

	Node s = node.next; // 獲取head的next節點,即佇列中第一個thread不為null節點
	if (s == null || s.waitStatus > 0) { // 節點為空或者waitStatus大於0時
		s = null; // s節點本就為null或者已被取消,從新設定為null
		// 從tail節點開始直至head節點
		// 為什麼要這樣操作呢???
		// 雖然s當前是null,
		for (Node t = tail; t != null && t != node; t = t.prev)
			if (t.waitStatus <= 0) // waitStatus不是取消時,設定s為該節點
				s = t;
	}
	// 當待喚醒節點不為null時,喚醒該節點
	if (s != null)
		LockSupport.unpark(s.thread);
}
// 取消當前執行緒節點
private void cancelAcquire(Node node) {
	if (node == null)
		return;

	node.thread = null; // 設定節點thread為null

	Node pred = node.prev;
	while (pred.waitStatus > 0)
		node.prev = pred = pred.prev;

	Node predNext = pred.next; // 最新pred節點的next節點

	node.waitStatus = Node.CANCELLED; // 當前節點設定為取消狀態

	// 如果當前節點是tail節點,則將tail節點指向最新得到的pred節點
	if (node == tail && compareAndSetTail(node, pred)) {
		compareAndSetNext(pred, predNext, null); // 設定最新tail節點的next節點為null
	} else {
		int ws;
		// tail節點不等於head節點 && (tail節點SIGNAL || tail的thread不為null)
		if (pred != head &&
			((ws = pred.waitStatus) == Node.SIGNAL ||
			 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
			pred.thread != null) {
			Node next = node.next;
			if (next != null && next.waitStatus <= 0)
				compareAndSetNext(pred, predNext, next);
		} else {
			// 喚醒第一個有效節點
			unparkSuccessor(node);
		}

		node.next = node; // help GC
	}
}