1.什麼是重入鎖
重入鎖就是指重複進入鎖,它表示該鎖能夠支持一個線程對資源的重複加鎖。
2.重入鎖所屬包
package java.util.concurrent.locks;
3.重入鎖繼承與實現關係
public class ReentrantLock implements Lock, java.io.Serializable
4.重入鎖的自定義同步器源碼
重入鎖ReentrantLock採用繼承的AQS的自定義的同步器來實現的。在自定義同步器的基礎上實現的公平鎖和非公平鎖的。
自定義同步器的實現類:
abstract static class Sync extends AbstractQueuedSynchronizer
自定義同步器實現方法:
nonfairTryAcquire() 方法:在獨佔模式、非公平模式下嘗試獲取同步狀態
/**
* 在獨佔模式,非公平模式下嘗試獲取同步狀態
*/
final boolean nonfairTryAcquire(int acquires) {
//獲取當前的線程current
final Thread current = Thread.currentThread();
//原子的方式獲取當前同步狀態值
int c = getState();
//如果同步狀態值爲初始化狀態
if (c == 0) {
//如果當前同步狀態值等於預期值0,那麼就將以原子的方式將當前同步狀態值設置爲更新值acquires
if (compareAndSetState(0, acquires)) {
//設置擁有獨佔訪問權限的線程爲當前線程
setExclusiveOwnerThread(current);
return true;
}
}
//如果當前線程爲擁有獨佔訪問權限的線程,這個代碼塊是當前線程重入情況下,同步狀態值增加acquires
else if (current == getExclusiveOwnerThread()) {
//當前同步狀態值加上獲取對象本身的狀態值
int nextc = c + acquires;
//如果最新同步狀態值小於0,拋出最大鎖數量超出
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//原子的方式設置最新同步狀態值
setState(nextc);
return true;
}
return false;
}
步驟解析:
<1>首先獲取當前線程,如果當前同步狀態值爲初始化狀態,就設置初始狀態值(acquires值),設置獨佔訪問線程的權限爲當前線程,返回true。
<2>如果當前同步狀態值已經被設置過了並且獨佔訪問線程的權限就是當前線程(鎖重入的情況了),那麼就將原本的同步狀態值加上鎖重入對象的狀態值作爲最新同步狀態值,然後設置同步狀態值,返回true。
<3>如果未設置成功並且當前線程不是獲取鎖的線程(獨佔式嘛),就返回false了。
tryRelease() 方法:在獨佔模式下,嘗試釋放同步狀態。
//在獨佔模式下,嘗試釋放同步狀態
protected final boolean tryRelease(int releases) {
//原子方式獲取當前同步狀態值減去對象本身已經獲取要釋放的狀態值
int c = getState() - releases;
//如果當前線程不是擁有獨佔訪問權限的線程,拋出非法主機狀態異常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
//釋放標誌位默認爲false
boolean free = false;
//如果釋放了的同步狀態值爲初始化狀態
if (c == 0) {
//設置釋放標誌位爲true
free = true;
//設置擁有獨佔訪問權限的線程爲null
setExclusiveOwnerThread(null);
}
//原子的方式更新最新的同步狀態值
setState(c);
//返回未完全釋放狀態值的標誌位false
return free;
}
步驟解析:
<1> 獲取釋放狀態值之後得到最新的同步狀態值 c。
<2> 如果釋放同步狀態值爲初始化狀態(說明沒有獲取鎖資源的線程了),則設置釋放標誌爲true,設置擁有獨佔訪問權限的線程自然也就是null了。
<3>更新同步狀態值爲最新的同步狀態值 c,返回釋放標誌位的值。
其他方法:
//當前的線程是否是以獨佔的方式進行的
protected final boolean isHeldExclusively() {
//擁有獨佔訪問權限的線程與當前線程比較,是否爲同一個
return getExclusiveOwnerThread() == Thread.currentThread();
}
//獲取當前同步狀態值
final int getHoldCount() {
//如果當前線程是以獨佔的方式運行,則得到當前同步狀態值,否則爲0(初始化的值)
return isHeldExclusively() ? getState() : 0;
}
5.什麼是公平鎖和非公平鎖
如果對鎖先請求的線程先被滿足就是公平鎖,而對鎖先請求沒有得到滿足,滿足條件是隨機的就是不公平鎖。這麼一想,那麼對於公平鎖,等待時間最長的線程最有可能獲取鎖,所以從此也會看出公平鎖是有順序獲取的。
6.非公平鎖的源碼
/**
* 獨佔模式下非公平鎖的實現
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* 當前線程獲取鎖資源
*/
final void lock() {
//如果當前同步狀態值等於預期值0,也就是說處於初始化的狀態,那麼就將當前同步狀態值更新爲1
if (compareAndSetState(0, 1))
//設置擁有獨佔訪問權限的線程爲當前線程
setExclusiveOwnerThread(Thread.currentThread());
else
//以獨佔的模式獲取對象,使用AQS的acquire方法。
acquire(1);
}
//是否允許在獨佔模式、非公平鎖的方式下獲取對象狀態
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
非公平鎖實現關鍵:
/**
* 在獨佔模式,非公平模式下嘗試獲取同步狀態
*/
final boolean nonfairTryAcquire(int acquires) {
//獲取當前的線程current
final Thread current = Thread.currentThread();
//原子的方式獲取當前同步狀態值
int c = getState();
//如果同步狀態值爲初始化狀態
if (c == 0) {
//如果當前同步狀態值等於預期值0,那麼就將以原子的方式將當前同步狀態值設置爲更新值acquires
if (compareAndSetState(0, acquires)) {
//設置擁有獨佔訪問權限的線程爲當前線程
setExclusiveOwnerThread(current);
return true;
}
}
//如果當前線程爲擁有獨佔訪問權限的線程,這個代碼塊是當前線程重入情況下,同步狀態值增加acquires
else if (current == getExclusiveOwnerThread()) {
//當前同步狀態值加上獲取對象本身的狀態值
int nextc = c + acquires;
//如果最新同步狀態值小於0,拋出最大鎖數量超出
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//原子的方式設置最新同步狀態值
setState(nextc);
return true;
}
return false;
}
nonfairTryAcquire 流程圖:
7.公平鎖的源碼
/**
* 獨佔模式下公平鎖的實現
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
//獨佔模式,當前線程獲取公平鎖
final void lock() {
acquire(1);
}
/**
* 在獨佔模式下,公平鎖的方式下獲取對象狀態
*/
protected final boolean tryAcquire(int acquires) {
//獲取當前線程
final Thread current = Thread.currentThread();
//原子的方式獲取當前同步狀態值
int c = getState();
//如果當前同步狀態值爲0,也就是初始化狀態
if (c == 0) {
//如果不存在等待時間更長的線程(就是同步隊列中當前節點是否有前驅節點)並且當前同步狀態值爲初始化狀態(更新同步狀態值爲acquires)
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
//設置擁有獨佔訪問權限的線程爲當前線程
setExclusiveOwnerThread(current);
return true;
}
}
//如果擁有獨佔訪問權限的線程是當前線程
else if (current == getExclusiveOwnerThread()) {
//計算最新的同步狀態值
int nextc = c + acquires;
//如果最新的同步狀態值小於0,那麼拋出超出最大鎖的數量
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
//原子的方式設置最新的同步狀態值
setState(nextc);
return true;
}
return false;
}
}
tryAcquire 流程圖:
8.重入鎖的構造方法
/**
* 實例化一個獨佔模式下的非公平鎖的對象
* 默認構造方法是採用非公平鎖方式
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* 如果傳入的參數fair爲true就創建公平鎖,否則就創建非公平鎖
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
9.重入鎖的方法
lock 方法:獲取鎖
public void lock() {
sync.lock();
}
tryLock 方法:
/**
* 查看該鎖沒有對其他線程持有,是則返回true,否則爲false
*/
public boolean tryLock() {
//自定義同步器的非公平方式獲取同步狀態
return sync.nonfairTryAcquire(1);
}
unlock 方法:
/**
* 嘗試釋放鎖
*/
public void unlock() {
sync.release(1);
}
10.閱讀總結
(1)ReentrantLock採用自定義同步器繼承AQS的方式來實現的。
(2)ReentrantLock分爲公平鎖和非公平鎖兩種獲取鎖的方式。
(3)ReentrantLock重入鎖中線程重進入的過程:就是先查看獲取鎖的線程是否爲當前佔據鎖的線程,是就先獲取它,然後採用當前佔據鎖的線程的同步狀態值加上重進入的當前線程對象的同步狀態值作爲最新同步狀態值來以原子的方式來更新當前同步狀態值。
(4)ReentrantLock重入鎖釋放鎖的過程:原子的方式獲取當前同步狀態值減去要釋放的同步狀態值的結果爲0,說明爲初始化狀態了,就釋放成功。(釋放鎖成功的條件就是最終的同步狀態值爲0)。