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

ReentrantLock源码分析(一)加锁流程分析

一、ReetrantLock的使用示例

static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        new Thread(ClassLayOutTest::reentrantLockDemo, "threadA").start();
        Thread.sleep(1000);
        new Thread(ClassLayOutTest::reentrantLockDemo, "threadB").start();
        Thread.sleep(1000);
        new Thread(ClassLayOutTest::reentrantLockDemo, "threadC").start();
    }

    public static void reentrantLockDemo() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + ": 获取到锁! -->" + System.currentTimeMillis() / 1000);
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

二、流程分析

上面的有三个线程都会去执行reentrantLockDemo()方法,在抢占到锁后会睡眠5S。然后在线程A启动后先睡眠1秒再启动B线程,线程B启动后睡眠1秒再启动线程C。这样就能保证了线程ABC按照固定顺序去抢占,那么程序执行的顺序是:

1、线程A抢占锁资源

线程A直接过来后,由于没有锁还没有被线程抢占。可以直接抢占到锁,线程A直接通过CAS将state的值由0修改为1,head及tail为null

2、线程B抢占失败进入AQS队列

线程B执行加锁操作的时候,由于锁已经被线程A占有了,所以没抢占到,执行进入AQS队列操作。具体的操作就是把自己封装成一个Node对象

放入到双向队列的时候,前面需要有一个伪头节点(节点中thread为null,waitStatus = -1),B放入进入后对应的node节点属性(thread = 线程B,waitStatus = 0)

3、线程B抢占失败,继续进入到AQS队列

线程C过来后,由于线程A已经抢占到锁资源,并且其业务代码需要执行5s中,那么此时线程c也无法抢到锁,需要进行进入AQS队列操作。在线程C进入AQS队列后,AQS目前现在应该有3个节点,伪节点 + Node(B) + Node(C)三个节点,状态如下所示:

最终我们看到示意图如下:

三、代码分析

1、lock方法分析

1.1、执行lock方法后,公平锁和非公平锁的执行方法不一样

// 非公平锁的实现
final void lock() {// 上来先尝试使用CAS的方法将0设置为1if (compareAndSetState(0, 1))//获取锁资源成功后,将当前线设置给属性字段exclusiveOwnerThreadsetExclusiveOwnerThread(Thread.currentThread());else// 尝试获取到1个资源acquire(1);
}

// 公平锁

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

1.2、acquire方法分析,这里的公平锁和非公平锁的逻辑是相同的

public final void acquire(int arg) {
    // tryAcquire:再次查看,当前线程是否可以尝试获取锁资源
    if (!tryAcquire(arg) &&
        // 没有拿到锁资源
        // addWaiter(Node.EXCLUSIVE):将当前线程封装为Node节点,插入到AQS的双向链表的结尾
        // acquireQueued:查看我是否是第一个排队的节点,如果是可以再次尝试获取锁资源,如果长时间拿不到,挂起线程
        // 如果不是第一个排队的额节点,就尝试挂起线程即可
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        // 中断线程的操作
        selfInterrupt();
}

1.3 、tryAcquire方法,,分为公平锁和非公平锁

// 非公平锁竞争锁资源逻辑
final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();// 获取当前state属性值 int c = getState();// state等于0的时候,说明之前持有锁的线程已经释放了锁资源if (c == 0) {// 基于CAS尝试将state由0设置为1if (compareAndSetState(0, acquires)) {// 设置互斥锁的拥有者为当前线程setExclusiveOwnerThread(current);return true;}}// 如果当前线程跟之前锁的拥有者是同一个,那么此处就是锁的重入else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;// 重入成功后,需要检查是否溢出if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");// 设置statu加1的值setState(nextc);return true;}return false;
}

-------------------------------------------------------------------------------------------------------------------------

// 公平锁实现逻辑

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.4、addWaiter方法分析

在没有拿到锁资源的时候,把线程放入到AQS队列中去

private Node addWaiter(Node mode) {// 将当前的线程封装成node对象,这里mode是互斥模式Node node = new Node(Thread.currentThread(), mode);Node pred = tail;if (pred != null) {node.prev = pred;if (compareAndSetTail(pred, node)) {pred.next = node;return node;}}enq(node);return node;
}
http://www.lryc.cn/news/38192.html

相关文章:

  • 【C++】list的模拟实现
  • Python连接es笔记三之es更新操作
  • 哪个牌子的蓝牙耳机音质好?音质比较好的蓝牙耳机排名
  • Qt实用技巧:Qt中浮点数的相等比较方式(包括单精度和双精度)
  • 【数据结构初阶】双向循环链表
  • 0104BeanDefinition合并和BeanClass加载-Bean生命周期详解-spring
  • Java集合进阶(三)
  • 【网络】什么是RPC?RPC与HTTP有什么关系?
  • [手撕数据结构]栈的深入学习-java实现
  • 2.线性表的顺序表示
  • eps文件删除了能恢复吗?恢复误删eps文件的三种方法
  • 【C++】运算符重载练习——Date 类
  • Redis学习(13)之Lua脚本【环境准备】
  • 关于BLE的一些知识总结
  • Spring框架源码分析一
  • CSS常用内容总结(扫盲)
  • Java启蒙之语言基础
  • 数据库系统--T-SQL数据查询功能-多表查询(超详细/设计/实验/作业/练习)
  • Spring Boot 3.0系列【14】核心特性篇之Configuration相关注解汇总介绍
  • [ubuntu][jetson]给jetson增加swap空间类似于给windows加虚拟内存
  • 小黑子—Java从入门到入土过程:第二章
  • ElasticSearch搜索详细讲解与操作
  • web实现太极八卦图、旋转动画、定位、角度、坐标、html、css、JavaScript、animation
  • 【LeetCode】33. 搜索旋转排序数组、1290. 二进制链表转整数
  • IBM Semeru Windows 下的安装 JDK 17
  • Lambda表达式和steram流
  • 面试必会-MySQL篇
  • Hadoop入门常见面试题与集群时间同步操作
  • JS 数组去重的方法
  • PMP项目管理项目沟通管理