通过下面的代码,除了理解可重入锁的原理,还可以更好的理解锁的争夺是基于CAS,为什么是不公平锁,为什么是悲观锁。
虽然synchronized关键字声明的锁是由jvm实现的,但是重量级锁的原理大同小异。synchronize不同的地方是,有个从偏向锁到重量级锁转变的过程,在重量级锁之前,锁的竞争完全由CAS决定。
class CustomReentrantLock implements Lock { // 自定义实现可重入锁
private AtomicReference<Thread> owner = new AtomicReference<>(); // 当前线程
private LinkedBlockingDeque<Thread> waiters = new LinkedBlockingDeque(); // 等待队列
private AtomicInteger count = new AtomicInteger(0); // 计算上锁次数
/**
* 先tryLock,如果不成功则放入阻塞队列
* 如果当前线程在阻塞队列头部,则tryLock,成功则从阻塞队列头部取出,否则挂起
*
*/
@Override
public void lock() { // park unpark方法与wait notify 最大的不同是不会死锁, 2组api不能混着用,无效果
if(!tryLock()) {
waiters.offer(Thread.currentThread()); // 加入等待队列尾部
while (true) { // 使用wile循环+tryLock实现自旋CAS操作,同时只要没获取就不会跳出循环,也就不会去执行锁内的代码,从而实现了锁控制
Thread head = waiters.peek(); // 查看头节点
if(head == Thread.currentThread()) {
if(tryLock()) {
waiters.poll(); // 获取到锁,从队列移除头元素
return; // 跳出循环
} else {
LockSupport.park(); // 没获得到锁,挂起当前线程
}
} else {
LockSupport.park();
}
}
}
}
/**
* 根据count是否为0判断锁是否被占用
* 被占用则根据owner判断是否可重复上锁,如果当前线程不是owner则进入waiters队列
* 没被占用则使用CAS上锁
* @return
*/
@Override
public boolean tryLock() {
int ct = count.get();
if (ct == 0) {
if (count.compareAndSet(ct, ct + 1)) {
owner.set(Thread.currentThread());
return true;
}
} else {
if (owner.get() == Thread.currentThread()) {
count.set(ct + 1);
return true;
}
}
return false;
}
@Override
public void unlock() { // 释放锁,然后唤起阻塞队列头部的线程,此时参与竞争锁的除了waiter队列的线程还有新创建的线程,这就是非公平锁的原因
if (tryUnlock()) {
Thread head = waiters.peek();
if (head != null) {
LockSupport.unpark(head);
}
}
}
/**
* 如果owner不是当前线程就抛异常
* 如果owner是当前线程,count计数减1,如果计数为0,使用CAS清空当前owner的值并且返回true
*/
public boolean tryUnlock() { // unlock的前提:当前线程一定是有锁的持有者,否则报错。只要锁持有者不释放锁,其他线程永远不能获得
if(Thread.currentThread() != owner.get()) {
throw new IllegalMonitorStateException();
} else {
int c = count.get();
int nextc = c - 1;
count.set(nextc);
if (nextc == 0) {
owner.compareAndSet(Thread.currentThread(), null);
return true;
} else {
return false;
}
}
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public Condition newCondition() {
return null;
}
}
网友评论