java之ReentrantLock

news2025/1/17 9:09:01

在讲RentrantLock之前需要先讲一下AQS和LockSupport,因为rentrantLock底层是用AQS实现的,而AQS中获取阻塞和唤醒底使用LockSupport实现的。

1、LockSupport实现

下面代码中,LockSupport.park方法是当前线程等待,直到获得许可,LockSupport.unpark方法则将线程作为参数传递,释放许可,让myThread能继续运行。

    static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println(Thread.currentThread() +": start park");
            LockSupport.park();
            System.out.println(Thread.currentThread() +": end park");
        }
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        System.out.println(Thread.currentThread() +": start unpark");
        LockSupport.unpark(myThread);
        System.out.println(Thread.currentThread() +": end unpark");
    }

park和unpark方法类似于object默认的wait和notify,区别在于:

1、Object.wait需要在同步代码块中执行,而park则不需要

2、wait当中断时,则会响应中断抛出中断异常,park不需要

3、object.wait是对象才能调用,而park则任何线程中都能调用

正因为park和unpark是对线程的阻塞和唤醒。适合运用到 ReentrantLock的并发线程中,其他线程阻塞和唤醒中。

2、AQS

网上有很多讲解AQS机制的,都列出一大堆源码,这里都不在多说源码了,简单说一下基本原理,至于细节部分,可以直接看源码后细扣。AQS核心思想是:如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。

2.1 AQS的数据结构

AQS底层数据结构是一个CLH的虚拟双向队列,实际上是一个双向链表,而请求共享需要等待的线程则会加入同步队列中,如果使用condition,则会加入到等待队列中,每一个线程使用以下图中Node节点表示。Node节点包含pre,next,当前线程,waitStatus。

其中waitStatus包含四种状态:

// CANCELLED,值为1,表示当前的线程被取消
static final int CANCELLED =  1;
// SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark 
static final int SIGNAL    = -1;
// CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中
static final int CONDITION = -2;
// PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行 
static final int PROPAGATE = -3;
值为0,表示当前节点在sync队列中,等待着获取锁

3、 ReentrantLock实现原理

知道了上述两个知识点之后,lock.lock和lock.unlock方法内部到底是怎么实现的呢?

    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        MyThread t1 = new MyThread("t1", lock);
        MyThread t2 = new MyThread("t2", lock);
        t1.start();
        t2.start();
    }
   static class MyThread extends Thread {
        private Lock lock;
        public MyThread(String name, Lock lock) {
            super(name);
            this.lock = lock;
        }

        @Override
        public void run() {
            lock.lock();
            try {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("thread ==== " + Thread.currentThread() + " running");
            } finally {
                lock.unlock();
            }
        }
    }

上述代码中t1 和t2同时执行,当t1执行lock之后,t2则等待lock释放锁,等到t1执行unlock之后,t2则被唤醒执行下面操作。内部具体流程是怎么样的?

1、当t1执行lock之后,由于此时只有t1这一个线程,则直接将此线程设置为独占锁

2、当t2执行lock之后, 由于t1已经是独占锁了。所以此时t2则需要加入到同步队列中

(1)先初始化同步队列,创建一个head节点,

(2)将head指向t2线程

(3)将head节点状态设置为SIGAL,当前节点的后继节点包含的线程需要运行,可以执行unpark

(4)对t2执行LockSupport.park(),阻塞t2线程

3、t1执行unlock,除了释放自身的独占锁之后,后续对同步队列中的t2线程也有相关操作

(1)从后往前找,找到状态设置为SIGAL的节点,是head节点,然后对后续节点执行unpark操作,上述我们知道unpark,则是唤醒t2线程

(2)将head节点状态设置为0

4、由于之前t2在lock过程中,底层AQS代码是一个自旋,不断获取资源,t2线程被唤醒执行后,将t2自身将清空,head指向t2, next为null;最终达到的状态是sync queue中只剩下了一个结点,并且该节点除了状态为0外,其余均为null。

5、t2执行unlock,最后的状态和之前的状态是一样的,队列中有一个空节点,头节点为尾节点均指向它。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1432431.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

06:原生云K8S解密|K8S集群安装部署|K8S网络插件

原生云K8S解密|K8S集群安装部署|K8S网络插件 K8SK8S集群架构图解 K8S部署仓库初始化kube-master安装计算节点的安装token管理 配置flannel网络(master主机操作) K8S 有大量夸主机的容器需要管理,快速部署应用&#xff…

问题:根据全面推进国防和军队现代化的战略安排,_____把人民军队全面建成世界一流军队。 #经验分享#媒体

问题:根据全面推进国防和军队现代化的战略安排,_____把人民军队全面建成世界一流军队。 A、2020年 B、2035年 C、本世纪中叶 D、2045年 参考答案如图所示 问题:判断题:高处作业传递物件应使用绳索,在确认作业下方…

面试数据结构与算法总结分类+leetcode目录【基础版】

🧡🧡🧡算法题目总结: 这里为大家总结数据结构与算法的题库目录,如果已经解释过的题目会标注链接更新,方便查看。 数据结构概览 Array & String 大家对这两类肯定比较清楚的,同时这也是面试…

C#,雅各布斯塔尔—卢卡斯(Jacobsthal Lucas Number)的算法与源代码

1 雅各布斯塔尔序列 雅各布斯塔尔序列是一个与斐波那契序列类似的加法序列,由递归关系JnJn-12Jn-2定义,初始项J00,J11。序列中的一个数字称为雅可布沙尔数。它们是卢卡斯序列Un(P,Q)的一种特殊类型&#x…

【stm32】hal库学习笔记-ADC模数转换(超详细!)

【stm32】hal库学习笔记-ADC模数转换(超详细!) 本篇章介绍了ADC实现电压检测的三种方式 ADC原理及选型 ADC将连续的模拟电压信号转换为二进制的数字信号 选型参数 速度(采样频率) 功耗 精度 转换原理 ADC hal库驱…

redis的缓存击穿和缓存雪崩和缓存穿透问题解决方法

Redis的缓存击穿: 热点的key,在不停的扛着大并发,当这个key失效时,一瞬间大量的请求冲到持久层的数据库中,就像在一堵墙上某个点凿开了一个洞! 解决方法: 1.热点key永不过期: 统计访…

【vue3学习P5-P10】vue3语法;vue响应式实现

0、vue2和vue3对比 框架版本API方式双向绑定原理domFragmentsTree-Shakingvue2选项式API(Options API)基于Object.defineProperty(监听)实现,不能双向绑定对象类型的数据【通过Object.defineProperty里面的set和get做…

【Java基础_02】Java变量

【Java基础_02】Java变量、运算符、程序控制结构 文章目录 1 变量1.1 程序中“”号的使用1.2 数据类型1.3 整数类型1.3.1 整数类型的分类1.3.2 整型的使用细节 1.4 浮点类型1.4.1 浮点型的分类1.4.2 浮点类型使用细节 1.5 字符类型1.5.1 字符类型使用细节1.5.2 字符类型本质1.5…

Mysql学习记录补充

索引 在无索引情况下,就需要从第一行开始扫描,一直扫描到最后一行,我们称之为 全表扫描,性能很低。 如果我们针对于这张表建立了索引,假设索引结构就是二叉树,那么也就意味着,会对age这个字段…

【FX110网】日交所发布1月交易数据:衍生品交易额达历年1月最高!

日本交易所集团(日交所,JPX)发布了其2024年1月的交易数据概览。数据显示,该交易所当月衍生品交易额创新历年来的1月交易数据最高纪录。2024年1月共有19个交易日。 2024年1月交易概览现货股票市场 2024年1月,该交易所主…

ArrayList在添加元素时报错java.lang.ArrayIndexOutOfBoundException

一、添加单个元素数组越界分析 add源码如下 public boolean add(E e) {ensureCapacityInternal(size 1); // Increments modCount!!elementData[size] e;return true; } size字段的定义 The size of the ArrayList (the number of elements it contains). ArrayList的大…

三层交换组网实验(华为)

思科设备参考:三层交换组网实验(思科) 一,技术简介 三层交换技术的出现,解决子网必须依赖路由器进行管理的问题,解决传统路由器低速、复杂所造成的网络瓶颈问题。一个具有三层交换功能的设备可简单理解为…

Unity引擎学习笔记之【角色按键器操作】

角色按键Character Controls 一、脚本操作 设置脚本 设置基本键盘操作 //水平轴float horizontal Input.GetAxis("Horizontal");//垂直轴float vertical Input.GetAxis("Vertical");//创建方向向量Vector3 dir new Vector3(horizontal,0,vertical);/…

《Python 网络爬虫简易速速上手小册》第5章:Python 数据存储与管理(2024 最新版)

文章目录 5.1 选择数据存储方案5.1.1 重点基础知识讲解5.1.2 重点案例:使用 SQLite 存储博客文章数据5.1.3 拓展案例 1:使用 MongoDB 存储社交媒体动态5.1.4 拓展案例 2:使用 Elasticsearch 存储和检索日志数据 5.2 数据清洗与预处理5.2.1 重…

鱼和熊掌如何兼得?一文解析RDS数据库存储架构升级

在2023年云栖大会上,阿里云数据库产品事业部负责人李飞飞在主题演讲中提到,瑶池数据库推出“DB存储”一体化能力,结合人工智能、机器学习、存储等方法和创新能力,实现Buffer Pool Extension能力和智能冷温热数据分层能力。在大会的…

Linux 高并发服务器

多进程并发服务器 使用多进程并发服务器时要考虑以下几点&#xff1a; 父进程最大文件描述个数(父进程中需要close关闭accept返回的新文件描述符)系统内创建进程个数(与内存大小相关)进程创建过多是否降低整体服务性能(进程调度) server /* server.c */ #include <stdio…

【刷题题解】最长回文子序列

给你一个字符串 s &#xff0c;找出其中最长的回文子序列&#xff0c;并返回该序列的长度。 子序列定义为&#xff1a;不改变剩余字符顺序的情况下&#xff0c;删除某些字符或者不删除任何字符形成的一个序列 这道题&#xff0c;一眼动态规划&#xff0c;但是即使动起来也规划…

TrinityCore安装记录

TrinityCore模拟魔兽世界&#xff08;World of Warcraft&#xff09;的开源项目&#xff0c;并且该项目代码广泛的优化、改善和清理代码。 前期按照官方手册按部就班的安装即可。 注意几点&#xff1a; 1 需要配置Ubuntu22.04版本的服务器或者Debian11 服务器。2 需要使用gi…

python_ACM模式《剑指offer刷题》链表4

题目&#xff1a; 面试tips&#xff1a; 询问是否需要判断环&#xff0c;可微调下方代码。 思路&#xff1a; 思路一&#xff1a; 判断环是否存在&#xff1a;设定一快一慢指针&#xff0c;均从头节点出发&#xff0c;快指针一次走两步&#xff0c;慢指针一次走一步。若无环…

docker安装-centos

Docker CE 支持 64 位版本 CentOS 7&#xff0c;并且要求内核版本不低于 3.10 卸载旧版本Docker sudo yum remove docker \ docker-common \ docker-selinux \ docker-engine使用yum安装 yum 更新到最新版本: sudo yum update执行以下命令安装依赖包&#xff1a; sudo yum…