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机制对于锁进行改进


分享到:


相關文章: