java 一步一步教你手寫ReentrantLock(二)

前言

繼上次,手寫了ReentrantLock ,但是仍然存在很多不完美的地方,以及很多漏洞,下面,我將著眼於漏洞處進行修補和升級。

上次源代碼

package top.gunplan.utils.lock;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
public class GunReLock implements Lock {
private AtomicReference<thread> owner = new AtomicReference<>(null);
private BlockingQueue<thread> waitqueue = new LinkedBlockingQueue<>();
@Override
public void lock() {
if (owner.compareAndSet(null, Thread.currentThread())) {
//獲取鎖成功
} else {
waitqueue.offer(Thread.currentThread());
LockSupport.park();
lock();
//獲取鎖失敗
}
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock() {
return false;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public void unlock() {
if (owner.compareAndSet(Thread.currentThread(), null)) {
LockSupport.unpark(waitqueue.poll());
} else {
//釋放鎖失敗,只有一種可能,當前線程不持有鎖
}
}
@Override
public Condition newCondition() {
return null;

}
}
/<thread>/<thread>
java 一步一步教你手寫ReentrantLock(二)

以下過程我們要對這個鎖進行升級

首先我們看到的是,trylock方法並沒有實現,我們現在來進行實現一下:

@Override
public boolean tryLock() {
return owner.compareAndSet(null, Thread.currentThread())
}

我們的try lock目前變成了這個樣子,trylock很簡單,只需要判斷能否替換成功就行,相應的lock方法發生了改變

if (tryLock()) {
//獲取鎖成功
} else {
waitqueue.offer(Thread.currentThread());
LockSupport.park();
lock();
//獲取鎖失敗
}

lock中使用了trylock()實現了複用。下一步要實現 trylock 在一定時間內的函數

第一步,計算出時間的毫秒數

第二步,循環trylock()直到超時

@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
long now = System.currentTimeMillis();
final long stoptime = unit.toNanos(time); //獲取所需要時間
while (System.currentTimeMillis() - now < stoptime) {
if (tryLock()) {
return true;
}
TimeUnit.MICROSECONDS.sleep(200);
}
return false;
}

寫到了這裡,還有一個非常大的bug沒有解決,也就是當lock()獲取鎖失敗之後,還沒有將thread加入等待隊列之前,鎖被釋放,waitqueue的隊首元素被喚醒,可是目前隊列中沒有元素,之後,調用lock的線程被加入隊列並且被park,這樣這個線程將會被永遠鎖住,再也見不到天日

public void lock() {
if (tryLock()) {
//快速獲取鎖成功
} else {
//#1
waitqueue.offer(Thread.currentThread());
if (tryLock()) {//再一次嘗試獲取鎖,防止unlock快速在#1過程中快速釋放
waitqueue.poll();
return;
} else {
//如果unlock在這裡快速釋放,queue 已經存在當前線程,
// 那麼因為park的順序無關性,所以並不會出現無盡等待
// 能夠被成功喚醒
LockSupport.park();
}

lock();
//獲取鎖失敗
}
}

下一節,我們使用aqs機制對於鎖進行改進


分享到:


相關文章: