ReentrantLock总结

简述

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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 作者: 一字马胡 转载标志 【2017-11-03】 更新日志 前言 在java中,锁是实现并发的关键组件,多个...
    一字马胡阅读 44,200评论 1 32
  • 前言 上一篇文章《基于CAS操作的Java非阻塞同步机制》 分析了非同步阻塞机制的实现原理,本篇将分析一种以非同步...
    Mars_M阅读 4,848评论 5 9
  • 博客链接:http://www.ideabuffer.cn/2017/03/15/深入理解AbstractQueu...
    闪电是只猫阅读 6,245评论 5 22
  • 孤独 衍生出漠视 漠视 助长了孤独 被抽空的世界 生怕谁闯进来 温暖了你 于是关上门
    尚晓阅读 235评论 1 3
  • 月已悬挂钩上楼台,春风对面吹夜已白。 舟似沉侧畔遥江面,两点雪飘飞奈何来。 情字寒凄切对长晚,思念如海水奔月怀。 ...
    昨夜星辰已泛黄阅读 220评论 0 2