前置思想
首先明確一點ReentrantLock與synchronized最大的不同點在于synchronized是鎖住了物件的頭部,而ReentrantLock是類似于我們買一把鎖,鎖住我們不想因為多執行緒下導致資源的原子性 ,可見性的問題,在明確這一點時我們的鎖其實與生活中的所沒太大區別,生活中的鎖無非就是大門掛一把鎖,這樣我們就告訴別人此處以上鎖,唯一不同的是程式鎖必須由上鎖的人來解鎖,而生活中我們可以持有別人的鎖進行開鎖,

主要使用到的類
- AtomicInteger:主要保證此鎖的原子性與可見性,以及涉及到后面的可重入鎖的狀態,注:volatile只能保證資源的可見性,并不能保證原子性
- Thread:當前那個執行緒持有這把鎖【PS:也就是誰持有了鎖,此處到后面可重入鎖,以及解鎖所需要的】
- LinkedBlockingQueue:沒搶到鎖物件的容器
- LockSupport:切換執行緒狀態
交代完了開懟!!!!
/**
* 執行緒狀態定義
*/
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:如有不對的地方請大手不吝嗇指出
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/109946.html
標籤:AI
上一篇:ESP8266環境搭建
下一篇:獨立看門狗
