当前位置: 首页 > news >正文

ReentrantLock源码解析

ReentrantLock源码解析

  • 概括
    • AQS实现
    • 构造方法
    • lock方法
      • 公平锁执行lock方法
        • 公平锁lock流程图
      • 非公平锁执行lock方法
        • 线程获取公平锁和非公平锁的区别
    • 释放锁


概括

ReentrantLock,是Lock接口的实现类,我们查看其内的一些方法

在这里插入图片描述
在这里插入图片描述
我们可以发现其内大部分方法都是通过sync对象来实现的,查看sync:
在这里插入图片描述
如上图,sync是一个抽象类,继承了AQS,而AQS的主要实现则是通过一个共享变量以及先进先出的队列实现,如下:

AQS实现

在这里插入图片描述
由volatile修饰的state,表示共享资源的状态,当其为0时表示资源空闲,>0时表示资源被锁定
在这里插入图片描述
而若有线程想要获取锁,就调用以上方法尝试使用CAS修改state变量,修改成功则表示成功获取锁,如果获取锁失败,线程就会被包装成结点加入先进先出的队列中,在AQS中该队列是通过双向链表实现的,如以下方法:
在这里插入图片描述

构造方法

我们已经知道大概知道了AQS的基本实现原理,而sync又继承了AQS。
查看其构造方法:

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

其包括两个构造方法,第一个构造方法不传入参数值,默认创建非公平锁,而第二个构造方法会根据传入的参数决定创建非公平锁还是公平锁,我们来看看什么是公平锁和非公平锁。
在这里插入图片描述
在这里插入图片描述
可以很清楚的看到非公平锁和公平锁都继承了Sync,并且它们存在两个相同的方法不同的具体实现。

lock方法

当调用lock方法时,会调用sync变量的lock方法,而Lock只是一个抽象类,调用时会根据不同的子类执行不同的方法
在这里插入图片描述

公平锁执行lock方法

公平锁会执行如下的方法:

    public final void acquire(int arg) {
        if (
    // 尝试获取锁。。。(1)
            !tryAcquire(arg) &&
    //如果获取锁失败将执行该方法.。。。(2)
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

查看(1):

        protected final boolean tryAcquire(int acquires) {
        // 获取当前线程
            final Thread current = Thread.currentThread();
        //获取状态码state
            int c = getState();
            //如果状态码为0表示锁是空闲的
            if (c == 0) {
            // 判断等待队列是否为null
                if (!hasQueuedPredecessors() &&
                //如果为null线程通过CAS修改state变量
                    compareAndSetState(0, acquires)) {
                    //如果修改成功设置持有锁的线程返回true
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 如果当前的线程就是持有锁的线程,就表示该线程重入这个锁
            else if (current == getExclusiveOwnerThread()) {
            //修改state变量
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded")
                    //通过CAS将state修改为重入的次数
                setState(nextc);
                return true;
            }
            return false;
        }
    }

我们进入以上代码的一些方法中查看:
该方法主要是判断队列中有没有其他要竞争锁的线程,如果存在当前线程就需要入队按照竞争锁的顺序来获取锁。

 public final boolean hasQueuedPredecessors() {
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return
        //如果等待队列的头节点就是尾结点,因为头节点是当前持有锁的线程,那么此时没有其他竞争锁的线程,当前线程有资格竞争锁
        h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

设置当前持有锁的线程:

    protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }

如果方法1返回true,则表明线程成功获取到了锁,如果返回false,则表示竞争锁失败,就会执行方法2,方法2中的参数调用了一个方法该方法会将当前线程包装成一个结点放到等待队列的尾部

    private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

然后执行以下方法,该方法会根据线程访问的顺序也即等待队列的顺序竞争锁

    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            //线程进入自旋状态
            for (;;) {
            //获取当前入队结点的前驱结点
                final Node p = node.predecessor();
                //当前驱结点为头节点时(即前驱结点当前持有了锁),当前线程才有获取锁的资格,当前线程通过CAS获取锁
                if (p == head && tryAcquire(arg)) {
                //当前线程获取锁成功后,将当前结点设置为头节点,返回false
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                //判断前驱结点的状态,如果状态为撤销,直接跳过
                if (shouldParkAfterFailedAcquire(p, node) &&
                //判断线程是否被中断
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
//通过方法二的返回值,判断当前线程的中断位,可以响应中断
   static void selfInterrupt() {
        Thread.currentThread().interrupt();
    }
公平锁lock流程图

在这里插入图片描述

非公平锁执行lock方法

     final void lock() {
     //直接CAS修改state状态,尝试获取锁
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
            //竞争锁失败
                acquire(1);
        }

竞争锁失败后的逻辑与公平锁的逻辑差不多,只有第一个方法是不同的

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

与公平锁不同的是,其发现state为0时直接CAS修改state获取锁

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

当线程获取不到锁时,会与线程竞争公平锁的逻辑相同,会包装为结点放入等待队列,锁定前驱结点,当前驱结点获取锁时才会尝试再次获取锁,而不是一直在等待队列中尝试获取锁,这样太浪费性能了。

线程获取公平锁和非公平锁的区别

线程调用lock方法获取公平锁时,当知道锁未被持有时,首先就会通过CAS修改state变量尝试获取锁,而非空锁则会判断等待队列中是否存在等待锁的线程,如果不存在会尝试获取锁。后面的逻辑两种则相同,加入等待队列,锁定前驱结点,当前驱节点持有锁时,会尝试获取锁。

至于超时获取锁的方法,大家可以看看源码,只比lock方法增加了一个超时的逻辑。

释放锁

释放锁的逻辑相当简单,用state变量减去当前线程重入锁的此时,使用CAS设置state变量即可

    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            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;
        }

相关文章:

  • PXE批量安装服务器
  • 微信小程序注册组件
  • python-leetcode-组合总和 Ⅳ
  • JDK ZOOKEEPER KAFKA安装
  • LeetCode 前缀和章节
  • uniapp或者vue 使用serialport
  • HTML第四节
  • java中实体类常见的设计模式
  • std::string的模拟实现
  • 基于混合蝴蝶粒子群算法 粒子群算法 蝴蝶算法实现无人机复杂山地环境下航迹规划附matlab代码
  • 《Linux C 智能 IO 矩阵:输入输出的自适应数据流转》
  • 如何设置爬虫的User-Agent?
  • 顺序表的插入、删除
  • 5分钟速览深度学习经典论文 —— attention is all you need
  • [AI相关]--可能不是0基础,但是很快解决c4d导出unity英雄联盟Lol模型问题
  • [记录与分享]如何保持充足的能量
  • Go学习笔记:Gin-路由
  • 3-9 WPS JS宏单元格复制、重定位应用(拆分单表到多表)
  • Next.js项目实战-ai助手帮我写文章发布视频第1节(共89节)
  • JavaScript 正则表达式全面指南
  • 南京建设工程交易中心网站/广告外链平台
  • java网站开发环境部署/站长工具查询网
  • wordpress主题the7.6/网站排名优化服务
  • 潍坊企业网站模板/网站批量查询
  • flash做网站的流程/seo技巧优化
  • 燕郊医疗网站建设/建网站多少钱