美文网首页
ReentrantLock总结

ReentrantLock总结

作者: 饭团爸爸v5 | 来源:发表于2017-06-23 10:02 被阅读70次

简述

ReentrantLock是java中非常重要的一个并发工具,相比于java原生的synchronized有着更好的性能

## 概念速查
ReentrantLock涉及的名称和概念较多,这里做一个简单的归类和解释,具体更为详细的内容,请自行Baidu或Google,这部分用于在阅读文章的时候,快速了解一些名称的概念,如果已经熟悉,请跳过。

快速预览

更强大的功能,玩玩意味着更为复杂的使用,ReentrankLock的使用比起synchronize,多了一个主动释放锁的代码,一个典型的使用示例如下

ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
    // ... method body
}finally {
    lock.unlock();
}

注意unlock的操作一定要置于finally块中,这样才能保证锁一定能释放。

uml图

看完了简单的使用示例,我们来快速的看一遍ReentrankLock的结构,下面是用idea的工具快速生成的uml图,感谢idea,大大提高了我们的工作质量。


ReentrantLock-UML.png

一次性过于深入的讨论,往往会迷失在繁琐的细节中,而难以把握全貌,而细节往往是由全局的目标决定的,所以我们一层一层的谈,不一次性深入最终代码。

由uml图,我们可以看出,ReentrantLock类是一个Lock接口的具体实现,每个ReentrantLock的实例,都持有一个sync对象,且这个sync是final修饰的,这个sync有两种具体的子类,分别是NonfairSync和FairSync,也就是非公平锁和公平锁。

ReentrantLock有两个构造方法,我们可以先看这两个方法,

public ReentrantLock() {
    sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
    sync = (fair)? new FairSync() : new NonfairSync();
}

可以看出,所谓构造函数,其实就是初始化需要使用的sync的类型,默认是非公平锁。参考公平锁与非公平锁

简要的预备知识

CAS

ReentrantLock的核心是对CAS方法的使用,现代的CPU提供了特殊的指令,可以自动更新共享数据,而且能够检测到其他线程的干扰,而 compareAndSet()使用了这些命令,具体请查阅CAS原理分析

wait

第一层解析

加锁部分

ReentrantLock的lock其实就是调用具体的sync的lock方法。
公平锁和非公平锁的加锁是有所不同的
对于公平锁来说如下

final void lock() {
    acquire(1);
}

对于非公平锁来说,是如下的

final void lock() {
  if (compareAndSetState(0, 1))
         setExclusiveOwnerThread(Thread.currentThread()); 
  else        acquire(1);
}

很显然,我们可以看出对于公平锁来说,非公平锁多了一个操作。我们首先来解释一下compareAndSetState方法,这个方法来自于sync继承的AbstractQueuedSynchronizer父类。state为0表示当前锁没有被占用,如果大于0,则表示被持有了,使用compareAndSetState,底层是个CAS方法,如果锁没有被占用,则置为占用状态,并且通过setExclusiveOwnerThread,将当前线程设置为该锁的持有者。

tryAcquire这个方法的作用,java的官方注释上如下写到
Acquires in exclusive mode, ignoring interrupts
大意是"尝试在独占模式下获取,忽略中断"。
截止到目前为止,我们看到的加锁过程的流程图如下,很简单

ReentrantLock第一层.png
acquire的工作

接下来,我们研究一下acquire做的工作

public final void acquire(int arg) {
    if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

tryAcquire,尝试将锁的状态置为arg,如果成功,则结束function,如果失败,在CHL队列中添加一个新的独占锁的节点,节点如果添加失败,则不做后续处理,如果成功则使用selfInterrupt将当前线程中断。流程图如下

解锁部分

我们跳过acquire具体做了什么,我们直接来看unlock做了什么。

public void unlock() {
    sync.release(1);
}
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;        
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;    
    }    
    return false;
}

相关文章

网友评论

      本文标题:ReentrantLock总结

      本文链接:https://www.haomeiwen.com/subject/qoqtrttx.html