ReentrantLock源码分析、LockSuppor、ReentrantReadWriteLock、锁优化的方法

ReentrantLock类图

在这里插入图片描述
在这里插入图片描述
我们看一下重入锁ReentrantLock类关系图,它是实现了Lock接口的类。NonfairSync和FairSync都继承 自抽象类Sync,在ReentrantLock中有非公平锁NonfairSync和公平锁FairSync的实现。
在重入锁ReentrantLock类关系图中,我们可以看到NonfairSync和FairSync都继承自抽象类Sync,而 Sync类继承自抽象类AbstractQueuedSynchronizer(简称AQS)。如果我们看过JUC的源代码,会发现 不仅重入锁用到了AQS, JUC 中绝大部分的同步工具也都是基于AQS构建的。

ReentrantLock源码分析:锁的获取

研究任何框架或工具都就要一个入口,我们以重入锁为切入点来理解AQS的作用及实现。下面我们深入ReentrantLock源码来分析AQS是如何实现线程同步的。

AQS其实使用了一种典型的设计模式:模板方法。我们如果查看AQS的源码可以看到,AQS为一个抽象 类,AQS中大多数方法都是final或private的,也就是说AQS并不希望用户覆盖或直接使用这些方法,而 是只能重写AQS规定的部分方法。

在这里插入图片描述

在这里插入图片描述
我们以重入锁中相对简单的公平锁为例,以获取锁的 lock 方法为入口,一直深入到AQS,来分析多线程 是如何同步获取锁的。
在这里插入图片描述
获取锁时源码的调用过程,时序图如下:

在这里插入图片描述

第一步:ReentrantLock.lock()

ReentrantLock获取锁调用了 lock 方法,我们看下该方法的内部:调用了sync.lock()。

public void lock() {
    sync.lock();
}

sync是Sync类的一个实例,Sync类实际上是ReentrantLock的抽象静态内部类,它集成了AQS来实现重 入锁的具体业务逻辑。AQS是一个同步队列,实现了线程的阻塞和唤醒,没有实现具体的业务功能。在 不同的同步场景中,需要用户继承AQS来实现对应的功能。

我们查看ReentrantLock源码,可以看到,Sync有两个实现类公平锁FairSync和非公平锁NoFairSync。

重入锁实例化时,根据参数fair为属性sync创建对应锁的实例。以公平锁为例,调用sync.lock事实上调用的是FairSync的lock方法。

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

第二步:FairSync.lock()

我们看下该方法的内部,执行了方法acquire(1),acquire为AQS中的final方法,用于竞争锁。

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

第三步:AQS.acquire(1)

线程进入AQS中的acquire方法,arg=1。
这个方法逻辑:先尝试抢占锁,抢占成功,直接返回;
抢占失败,将线程封装成Node节点追加到AQS队列中并使线程阻塞等待。
(1)首先会执行tryAcquire(1)尝试抢占锁,成功返回true,失败返回false。抢占成功了,就不会执行 下面的代码了
(2)抢占锁失败后,执行addWaiter(Node.EXCLUSIVE)将x线程封装成Node节点追加到AQS队列。
(3)然后调用acquireQueued将线程阻塞,线程阻塞。 线程阻塞后,接下来就只需等待其他线程唤醒它,线程被唤醒后会重新竞争锁的使用。 接下来,我们看看这个三个方法具体是如何实现的。

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

第四步:FairSync.tryAcquire(1)

尝试获取锁:若获取锁成功,返回true;获取锁失败,返回false。
这个方法逻辑:获取当前的锁状态,如果为无锁状态,当前线程会执行CAS操作尝试获取锁;若当前线 程是重入获取锁,只需增加锁的重入次数即可。

在这里插入图片描述

第五步:AQS.addWaiter(Node.EXCLUSIVE)

线程抢占锁失败后,执行addWaiter(Node.EXCLUSIVE)将线程封装成Node节点追加到AQS队列。
addWaiter(Node mode)的mode表示节点的类型,Node.EXCLUSIVE表示是独占排他锁,也就是说重入 锁是独占锁,用到了AQS的独占模式。
Node定义了两种节点类型:

  • 共享模式:Node.SHARED。共享锁,可以被多个线程同时持有,如读写锁的读锁。
  • 独占模式:Node.EXCLUSIVE。独占很好理解,是自己独占资源,独占排他锁同时只能由一个线程 持有。
staticfinalNodeSHARED=newNode();//共享模式
staticfinalNodeEXCLUSIVE=null;//独占模式

相应的AQS支持两种模式:支持独占模式和共享模式。
在这里插入图片描述

第六步:AQS.acquireQueued(newNode,1)

在这里插入图片描述
在这里插入图片描述

AQS.shouldParkAfterFailedAcquire

在这里插入图片描述
在这里插入图片描述

AQS.parkAndCheckInterrupt

在这里插入图片描述

LockSupport类

在这里插入图片描述

ReentrantLock源码分析:锁的释放

在这里插入图片描述

第一步:ReentrantLock.unlock

在这里插入图片描述

第二步:AQS.release(1)

在这里插入图片描述

第三步:Sync.tryRelease(1)

在这里插入图片描述

第四步:AQS.unparkSuccessor

在这里插入图片描述

第五步:LockSupport.unpark(s.thread)

会唤醒挂起的线程,使被阻塞的线程继续执行。

公平锁和非公平锁源码实现区别

公平锁和非公平锁在获取锁和释放锁时有什么区别呢?

  • 非公平锁与非公平锁释放锁是没有差异,释放锁时调用方法都是AQS的方法。
  • 非公平锁与非公平锁获取锁的差异
    我们可以看到上面在公平锁中,线程获得锁的顺序按照请求锁的顺序,按照先来后到的规则获 取锁。如果线程竞争公平锁失败后,都会到AQS同步队列队尾排队,将自己阻塞等待锁的使用 资格,锁被释放后,会从队首开始查找可以获得锁的线程并唤醒。
    而非公平锁,允许新线程请求锁时,可以插队,新线程先尝试获取锁,如果获取锁失败,才会 AQS同步队列队尾排队。

我们对比下两种锁的源码,非公平锁与非公平锁获取锁的差异有两处:

  1. lock方法差异:
    在这里插入图片描述
  2. tryAcquire差异
    FairSync.tryAcquire:公平锁获取锁,若锁为无锁状态时,本着公平原则,新线程在尝试获得锁前,需 先判断AQS同步队列中是否有线程在等待,若有线程在等待,当前线程只能进入同步队列等待。若AQS 同步无线程等待,则通过CAS抢占锁。而非公平锁,不管AQS是否有线程在等待,则都会先通过CAS抢占 锁。
    在这里插入图片描述

读写锁ReentrantReadWriteLock

可重入锁ReentrantLock是互斥锁,互斥锁在同一时刻仅有一个线程可以进行访问,但是在大多数场景 下,大部分时间都是提供读服务,而写服务占有的时间较少。然而读服务不存在数据竞争问题,如果一 个线程在读时禁止其他线程读势必会导致性能降低,所以就出现了读写锁。
读写锁维护着一对锁,一个读锁和一个写锁。通过分离读锁和写锁,使得并发性比一般的互斥锁有了较 大的提升:在同一时间可以允许多个读线程同时访问,但是在写线程访问时,所有读线程和写线程都会 被阻塞。
读写锁的主要特性:

  • 公平性:支持公平性和非公平性。
  • 重入性:支持重入。读写锁最多支持65535个递归写入锁和65535个递归读取锁。
  • 锁降级:写锁能够降级成为读锁,但读锁不能升级为写锁。遵循获取写锁、获取读锁在释放写锁的 次序

读写锁ReentrantReadWriteLock实现接口ReadWriteLock,该接口维护了一对相关的锁,一个用于只读 操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持。写入锁是独 占的。
在这里插入图片描述

ReadWriteLock定义了两个方法。readLock()返回用于读操作的锁,writeLock()返回用于写操作的锁。 ReentrantReadWriteLock定义如下:
在这里插入图片描述
ReentrantReadWriteLock与ReentrantLock一样,其锁主体依然是Sync,它的读锁、写锁都是依靠 Sync来实现的。所以ReentrantReadWriteLock实际上只有一个锁,只是在获取读取锁和写入锁的方式 上不一样而已,它的读写锁其实就是两个类:ReadLock、writeLock,这两个类都是lock的实现。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

锁优化

减少锁持有时间

在这里插入图片描述

减少锁粒度

  • 将大对象拆分成小对象,增加并行度,降低锁竞争。
  • ConcurrentHashMap允许多个线程同 时进入

锁分离

  • 根据功能进行锁分离
  • ReadWriteLock在读多写少时,可以提高性能。

锁消除

  • 锁消除是发生在编译器级别的一种锁优化方式。
  • 有时候我们写的代码完全不需要加锁,却执行了加锁操作。

锁粗化

通常情况下,为了保证多线程间的有效并发,会要求每个线程持有锁的时间尽可能短,但是在某些情况下,一个程序对同一个锁不间断、高频地请求、同步与释放,会消耗掉一定的系统资源,因为锁的请求、同步与释放本身会带来性能损耗,这样高频的锁请求就反而不利于系统性能的优化了,虽然单次同步操作的时间可能很短。锁粗化就是告诉我们任何事情都有个度,有些情况下我们反而希望把很多次锁的请求合并成一个请求,以降低短时间内大量锁请求、同步、释放带来的性能损耗。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Rust-获取随机数练习案例

文章目录 前言一、取官网示例猜数字游戏玩一玩cargo checkTOML文件 二、完整代码总结 前言 Rust学习系列-获取随机数练习案例,基于cargo 进行案例练习,过程中会使用cargo check; cargo run;等命名,同时了解基础的let …

flask_django_python五金电商网络营销的可视化分析研究

前面部分完成了系统需求分析,了解到新闻数据业务方面的需求,系统主要分为用户管理、五金信息管理、在线留言、系统管理等功能。销的可视化研究,并对这些数据进行处理, 然后对这些数据进行可视化分析和统计。 Python 爬虫技术目前来…

linux免密登录的实现

ssh免密登录使用方便,关键没有了口令验证反倒规避了暴力破解或者被探测的风险。配置得当,使用ssh免密登录更加安全。在生产环境中应用和数据库服务器之间互相设置后使用方便,并且在第三方人员配置使用时不用告知对方密码。 第一步、ssh登录发…

如何在HA智能家居系统中添加HACS集成并实现异地控制家中苹果与小米设备

文章目录 基本条件一、下载HACS源码二、添加HACS集成三、绑定米家设备 ​ 上文介绍了如何实现群晖Docker部署HomeAssistant,通过内网穿透在户外控制家庭中枢。本文将介绍如何安装HACS插件商店,将米家,果家设备接入 Home Assistant。 基本条件…

第十三篇【传奇开心果系列】Python的OpenCV库技术点案例示例:光流估计

传奇开心果短博文系列 系列短博文目录Python的OpenCV库技术点案例示例:光流估计短博文目录前言一、光流估计介绍二、Lucas-Kanade光流介绍和示例代码三、Horn-Schunck光流介绍和示例代码四、cv::calcOpticalFlowPyrLK()函数实现光流估计介绍和示例代码五、光流估计用于运动分析…

Express框架介绍—node.js

Express—Node.js 官网传送门(opens new window) 基于 Node.js 平台,快速、开放、极简的 Web 开发框架 Express 是用于快速创建服务器的第三方模块。 Express 初体验 基本使用 安装 Express: npm install express创建服务器,监听客户端请…

瑞萨RA6M3开发实践指南-UART实践

1.背景说明 本文是参考瑞萨RA6M3开发实践指南文章教程,基于瑞萨HMI-Board BSP :1.1.1 版本 RT-Thread 5.0.1 版本操作步骤进行记录,整理成的文档。 1.1 本章内容 使用RT-Thread Studio创建开发板的程序,编写UART的程序,实现串口…

跟着pink老师前端入门教程-day19

一、移动WEB开发之流式布局 1、 移动端基础 1.1 浏览器现状 PC端常见浏览器:360浏览器、谷歌浏览器、火狐浏览器、QQ浏览器、百度浏览器、搜狗浏览器、IE浏览器。 移动端常见浏览器:UC浏览器,QQ浏览器,欧朋浏览器&#xff0…

Yolo v8 进行对象数量统计示例

Yolo v8 进行对象数量统计示例 示例代码 from ultralytics import YOLO from ultralytics.solutions import object_counter import cv2def object_count_test():# 权重文件,可替换为自己训练的权重文件model YOLO("yolov8n.pt")# results model.trai…

【码农新闻】 用HTTPS,还能被查出浏览记录吗 常用且好用的在线工具......

目录 【码农新闻】 用HTTPS,还能被查出浏览记录吗 常用且好用的在线工具...... 用HTTPS,还能被查出浏览记录吗常用且好用的在线工具尤雨溪 文章所属专区 码农新闻 欢迎各位编程大佬,技术达人,以及对编程充满热情的朋友们&#xf…

锁优化的方法

减少锁持有时间 减少锁粒度 将大对象拆分成小对象,增加并行度,降低锁竞争。ConcurrentHashMap允许多个线程同 时进入 锁分离 根据功能进行锁分离ReadWriteLock在读多写少时,可以提高性能。 锁消除 锁消除是发生在编译器级别的一种锁优化…

C语言数组练习以及场景练习题

写了那么久的知识点梳理,今天来写点自己觉得不错的练习题来分享,顺便来巩固自己的知识点,和加强题型的解决方法的记忆。今天给大家带来的有数组的找数字题目,以及场景找凶手的题目,下面让我们来看看今天的第一道题目。…

一文带你读懂编码和解码

编码和解码 编码:将字符转换为对应的二进制序列的过程叫做字符编码。 解码:将二进制序列转换为对应的字符的过程叫做字符解码。 py文件的创建: 文本编辑器在内容没有保存前,内容以编辑器默认编码形式保存在内存中。pycharm默认…

我要成为嵌入式高手之2月4、5日Linux高编第二天!!

读写函数接口 1、fgetc 语法&#xff1a; 功能&#xff1a;从流中获取一个字符 练习1&#xff1a;编写程序统计文件的行数 #include <stdio.h>int main(void) {FILE *fp NULL;char ch 0;int row 0;fp fopen("file.txt","r");if (NULL fp){pe…

5 分钟让你了解什么是搜索引擎

文章目录 搜索引擎概述基于业务模式分类垂直搜索&#xff08;垂搜&#xff09;通用搜索&#xff08;通搜&#xff09;本地搜索引擎 基于技术实现分类基于关键词的搜索引擎&#xff08;Keyword-based Search Engine&#xff09;语义搜索引擎&#xff08;Semantic Search Engine&…

好用的大学生教材搜题软件?帮助大学生解决问题的实用搜题工具 #知识分享#知识分享#经验分享

对于大学生来说&#xff0c;每天面对各式各样的学习任务和问题&#xff0c;寻找合适的学习资源和工具成了我们的迫切需求。幸运的是&#xff0c;现如今有许多高效且实用的日常搜题和学习软件可以满足我们的需求&#xff0c;助力我们取得更好的学习成果。 1.Forest专注森林 Fo…

力扣:42. 接雨水 84.柱状图中最大的矩形(单调栈,双指针)

这两道题解题思路类似&#xff0c;一个是单调递增栈&#xff0c;一个是单调递减栈。本篇博客给出暴力&#xff0c;双指针和单调栈解法。 42. 接雨水 题目&#xff1a; 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后…

CIFS(Samba)服务的使用

理论部分 概念&#xff1a; 通用互联网文件系统CIFS使用的是公共的或者开放的SMB协议版本。SMB是在会话层和表示层以及小部分应用层上的 协议&#xff0c;使用了NetBIOS的应用程序接口API。该协议在局域网上用于服务器文件访问和打印。它使用客户/服务器模式&#xff0c;客 户…

ffmpeg的使用,安装,抽帧,加水印,截图,生成gif,格式转换,抓屏等

实际使用中总结的关于ffmpeg对视频的处理的记录文档 具体信息&#xff1a; http://ffmpeg.org/download.html 官网下载ffmpeg 关于ffmpeg的安装详细步骤和说明 装ffmpeg 方式,Linux和windows下的 http://bbs.csdn.net/topics/390519382 php 调用ffmpeg , http://bbs.csdn.net/t…

【算法】利用递归dfs解决二叉树算法题(C++)

文章目录 1. 前言2. 算法题2331.计算布尔二叉树的值129.求根节点到叶节点数字之和LCR047.二叉树剪枝98.验证二叉搜索树230.二叉搜索树中第K小的元素257.二叉树的所有路径 1. 前言 有关 递归 的相关解释与解题 请看下文&#xff1a; 以汉诺塔理解递归、并用递归解决算法题 对于…