首先明確一點ReentrantLock與synchronized最大的不同點在於synchronized是鎖住了物件的頭部,而ReentrantLock是類似於我們買一把鎖,鎖住我們不想因為多執行緒下導致資源的原子性 ,可見性的問題,在明確這一點時我們的鎖其實與生活中的所沒太大區別,生活中的鎖無非就是大門掛一把鎖,這樣我們就告訴別人此處以上鎖,唯一不同的是程式鎖必須由上鎖的人來解鎖,而生活中我們可以持有別人的鎖進行開鎖。
/**
* 執行緒狀態定義
*/
AtomicInteger state = new AtomicInteger();
/**
* 那個執行緒持有鎖
*/
Thread ownerThread = null;
/**
* 存放執行緒的容器
*/
LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue<>();
這步沒啥可說的繼續!!
public boolean tryLock() {
//不用每次都進行CAS操作,如果當期狀態為0我直接返回搶鎖成功
if (state.get() == 0) {
//CAS搶鎖
if (state.compareAndSet(0, 1)) {
//將搶到鎖的執行緒設定為當前鎖的持有者
ownerThread = Thread.currentThread();
return true;
}
} else if (ownerThread == Thread.currentThread()) {
System.out.println("重入鎖成功!!!!");
state.set(state.get() + 1);
return true;
}
return false;
}
此處主要用到了CAS機制,如果修改成功證明已搶到鎖,失敗則加入佇列,以及重入鎖如果是當前執行緒那麼獲取鎖成功,並且在狀態+1【PS:此處狀態+1主要是為了可重入鎖,應為重入鎖的本質需要你加鎖幾次就要釋放幾次】
public void lock() {
//真公平鎖 如果沒有!waiters.isEmpty(),在鎖的上一個持有者他也會搶
if (!waiters.isEmpty() || !tryLock()) {
//沒搶到鎖進入等待佇列
waiters.add(Thread.currentThread());
for (; ; ) {
if (tryLock()) {
//移除並且返回佇列頭部
waiters.poll();
return;
} else {
//沒搶到 進入阻塞(WAITING)
LockSupport.park();
}
}
}
}
如果當前沒有執行緒持有鎖則直接返回,有則將他加入佇列並且阻塞【PS:當然一些猿認為使用LinkedBlockingQueue不就已經是公平鎖了麼(先入先出),其實不然比如:有三個執行緒,1搶鎖成功,在釋放後其實1也參與搶鎖了,也就是插隊!!此處的插隊就導致看似公平的鎖變的不公平了】
public void unlock() {
//如果不是當前執行緒那麼不允許釋放鎖
if (Thread.currentThread() != ownerThread) {
throw new RuntimeException("不是你的別亂動,你不是鎖的持有者");
}
//釋放鎖的過程,每次過來減一
if (state.decrementAndGet() == 0) {
ownerThread = null;
//獲取第一個等待的元素並且通知他開始搶鎖了,此時還不能移除
//在真正搶到鎖後才能移除佇列
Thread waiter = waiters.peek();
if (waiter != null) {
LockSupport.unpark(waiter);
}
}
}
此處我們釋放鎖的時候必須是當前持有鎖的執行緒,如果其他執行緒來釋放鎖呢就亂套了
PS:如有不對的地方請大手不吝嗇指出