手写非公平锁(ReentrantLock)
手写非公平锁(ReentrantLock)
ReentrantLock很多教程都在讲解,虽然很细致,但内容太多了,过段时间就很容易忘,我们知道ReentrantLock的核心是AQS(抽象队列同步器),我们这里试着使用AQS,自己写一个非公平锁使用,看看AQS承载了哪部分的职责,ReentrantLock承载了哪部分的职责
第一步:定义接口,面向接口编程嘛
public interface myLock {
// 很简单的加锁和释放锁
public void lock();
public void unlock();
}
第二步:看看ReentrantLock与AQS的关系
public class myUnFairLock implements myLock {
private final Sync sync = new Sync();
// 锁的核心由AQS管理,ReentrantLock只负责重写 tryAcquire 和 tryRelease方法的逻辑
private static class Sync extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {}
@Override
protected boolean tryRelease(int arg) {}
public void lock() {
// 这些方法AQS都已经封装好,我们只需要组合即可
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
} else {
acquire(1);
}
}
public void unlock(release(1);)
}
@Override
public void lock() {
sync.lock();
}
@Override
public void unlock() {
sync.unlock();
}
}
第三部:查看ReentrantLock重写的加锁,释放锁的步骤
private static class Sync extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取同步器的状态值(AQS初始好的),其实就是一个锁的引用计数器,为0时表示锁无人占有
int state = getState();
// 说明state值是与锁状态有关的
if (state == 0) {
// 0代表可以再次抢锁
if (compareAndSetState(0, arg)) {
// 如果抢到,指明当前线程,获取到锁了
setExclusiveOwnerThread(current);
return true;
}
}
// 判断占有线程是否就是自己,保证锁的可重入性
else if (current == getExclusiveOwnerThread()) {
// 如果是自己的锁,则计数器+1
int newState = state + arg;
// 这里是怕无限递归,造成计数器溢出
if (newState < 0) {
throw new Error("Maximum lock count exceeded");
}
// 设置新的值
setState(newState);
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
// 获取锁的引用计数器
int c = getState() - arg;
// 判断清楚现在释放的锁是自己占用的锁不
if (Thread.currentThread() != getExclusiveOwnerThread()) {
throw new IllegalMonitorStateException();
}
boolean free = false;
if (c == 0) {
// 锁状态清0
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
// 如果锁还有被引用,就是false,无引用就是true
return free;
}
public void lock() {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
} else {
acquire(1);
}
}
public void unlock() {
release(1);
}
}
这些就是ReentrantLock核心做的事儿,其他的都交给AQS来完成,例如设置锁占有,获取锁值,通过原子比较尝试获取锁,阻塞线程的管理等。如果需要我们可以在中间加入我们的逻辑,来替代原有的ReentrantLock。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 玲辰书斋!