ReentrantLock实现机制

掌握Reentrantlock

具体结构

下文Reentrantlock简称RL,阅读之前强烈建议读一下AQS源码解析: https://www.cnblogs.com/seamount3/p/18690818

其实RL和AQS有关系,但不是直接有关系,是RL内部有一个Sync变量,RL其实是实现LOCK接口好来实现对应的LOCK方法

何为Sync,其实是一个内部类 extends AQS,内部又有两个子类分别是公平锁与非公平锁

RL的核心就是在对应的内部类Sync的子类上

子类之FairSync

lock

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

没太多好说的,其实就只是包装了一下,调用AQS的 acquire方法

相对有意思的就是为什么要是1?

1其实是AQS抽象出来的参数,在此地是代表线程请求锁时需要获取的许可数量

这里的 1 表示当前线程请求 一个 锁的许可,锁的操作是以许可为基础的。

具体看下面的tryAcquire

tryAcquire

protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}

相对简单,流程如下:

  1. 获取当前state变量
    1. state=0,判断CLH是否有节点
      1. 如果没有那么进行CAS操作state
      2. 操作成功的话,设置为独占线程
  2. 为了实现可重入,那么还要考虑是否独占线程为当前线程
    1. 是的话,int nextc = c + acquires;并且setState

子类之NonfairSync

lock

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

tryAcquire

和公平锁差不多,只不过不进行判断是否有前驱节点

Sync父类实现了什么功能?

核心之tryRelease

本方法的实现是为了给AQS实现对应的钩子方法,在RL的unlock方法最终会调用AQS的release

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;
}

核心的流程如下:

  1. 判断当前线程是否是锁资源持有的线程
    1. 不是的话,抛异常
  2. 1ok后,修改state
    1. 如果state改完==0,那么彻底释放锁
    2. 如果不等于只是修改state,不释放锁,为了支持可重入嘛

如果面试官让介绍ReentrantLock如何介绍?

  1. 快速简单介绍(包括功能)
  2. 详细介绍下如何实现

快速简单介绍(包括功能)

首先ReentrantLock实现了对应的Lock接口,他的功能基本上起到的就是本地锁的作用,

ReentrantLock在功能上支持:

  1. 互斥性(RL是互斥锁,同一时刻只允许一个线程获取锁进入临界区,写操作自然也是互斥的。)
  2. 可重入性
  3. 公平与非公平
  4. 可中断,lockInterruptibly() 方法,允许线程在等待锁的过程中被中断
  5. 限时性,例如TryLock可以允许线程在指定的时间内尝试获取锁。如果在指定时间内没有获取到锁,线程会返回 false

上述的功能大多是借助AQS的已有代码,以及ReentrantLock内部类像FairSync等继承了AQS并且实现了对应的钩子方法例如tryAcquire来实现总体的功能

详细介绍下如何实现

讲一下lock流程:

  1. 首先入口是RL的lock方法,而底层其实是调用内部变量Sync的acquire方法(也就是AQS的acquire模板方法)
  2. 这里就体现出公平与非公共的区别了,在构造器中我们会生成公平锁的实例或者非公平锁的实例通过这一点就实现了公平与非公平
  3. 以公平为例,那么我们会进行AQS的acquire流程
    1. 先调用对应公平锁实现的tryAcquire
      1. 获取当前state变量
        1. state=0,判断CLH是否有节点
          1. 如果没有那么进行CAS操作state
          2. 操作成功的话,设置为独占线程
      2. 为了实现可重入,那么还要考虑是否独占线程为当前线程
        1. 是的话,int nextc = c + acquires;并且setState
  4. 如果tryAcquire失败,那么就进入把当前线程转换为Node添加到CLH队尾流程,然后等待唤醒,然后对应还有一个重要的方法就是acquireQueued,相当于是死循环,然后每个线程会再次tryAcquire如果失败再阻塞,之后就等待唤醒

讲一下unlock流程:

  1. 内部会调用对应Sync类的release方法(也就是AQS实现的模板方法)
  2. 然后执行对应的tryRelease流程
  3. 最后唤醒队列中的下一个节点

而刚刚提到的可中断也是AQS锁支持的,以及超时机制,都是AQS在特定代码,例如可中断则是在循环中会去判断下线程状态而超时则是在循环过程中结合当前时间与起始时间之间求差进行判断

posted @   海山了-  阅读(127)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示