我们先看顶层接口逻辑
图1
可以看到最顶层的接口分为Lock和ReadWriteLock,其中ReentrantLock实现了Lock接口,而ReentrantReadWriteLock实现的是ReadWriteLock接口,本文主要分析ReentrantLock。
ReentrantLock和synchronized我们知道都有锁的功能,只不过ReentrantLock比synchronized更加灵活,可以实现尝试加锁,锁中断等功能。
一 初始化锁
@Service
public class TestLock {
private static final Lock lock = new ReentrantLock();
public void getLock(){
System.out.println(Thread.currentThread().getName()+":获取锁");
System.out.println("内存地址:"+VM.current().addressOf(lock));
lock.lock();
try{
System.out.println("qwe");
Thread.sleep(5000L);
}catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
System.out.println(Thread.currentThread().getName()+":释放锁");
}
}
}
当代码运行到 new ReentrantLock()这行代码时虚拟机会将ReentrantLock对象实例化,我们看下实例化方法
图2
可以看到不加参数的实例化创建的是非公平锁,我们看非公平锁的实例化
图3
我们再看 Sync继承自AbstractQueuedSynchronizer,AbstractQueuedSynchronizer里state字段会被赋初始值0,这个字段是加解锁的关键,先有个印象。
二 加锁
当代码运行到lock.lock()的时候我们跟进去
public final void acquire(int arg) {
//尝试加锁
if (!tryAcquire(arg) &&
//如果加锁失败就放入队列
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//如果加锁失败并且已经放入队列就中断当前线程
selfInterrupt();
}
当第一个线程调用 lock.lock()的时候,尝试加锁肯定成功 acquire方法正常结束lock方法也正常结束,进入业务代码,此时第二个线程在第一个线程还没释放锁就进入到lock.lock()方法时,尝试加锁肯定失败就会addWaiter和acquireQueued 方法,我们看addWaiter和acquireQueued做了什么
private Node addWaiter(Node mode) {
//将当前线程封装成一个node节点
Node node = new Node(Thread.currentThread(), mode);
//取出aqs等待队列里的尾节点
Node pred = tail;
//如果aqs等待队列里的尾节点不为空,
if (pred != null) {
//则将当前线程node节点的头节点设置为aqs等待队列里的尾节点
node.prev = pred;
//采用cas模式将aqs等待队列里的尾节点设置为当前线程的node节点
if (compareAndSetTail(pred, node)) {
//将aqs等待队列里的尾节点的下一节点指向当前线程的node节点
pred.next = node;
return node;
}
}
//如果aqs等待队列里的尾节点为空,将当前线程的node节点放入队列
enq(node);
return node;
}
节点封装完了就是放入aqs队列了,我们看acquireQueued方法
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//取出当前节点的前置节点
final Node p = node.predecessor();
//如果前置节点==头节点就开始尝试加锁
if (p == head && tryAcquire(arg)) {
//如果加锁成功将当前节点设置为头节点
setHead(node);
//将原来的头节点值为空帮助gc
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
三 解锁
public final boolean release(int arg) {
//尝试解锁
if (tryRelease(arg)) {
Node h = head;
//如果头节点不为空,并且头节点的waitStatus不等于0,需要
//将头节点的waitStatus 置为0,表示已解封该节点,等待下次获取锁
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
尝试解锁代码如下
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
//判断当前线程是否持有锁
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
//将当前线程改为不在持有锁
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
以上是个人对ReentrantLock的解读,希望有帮助