AQS和ReentrantLock源码剖析

news2024/11/25 14:52:58

AbstratQuenedSynchronizer抽象队列同步器

一. 简单理解

源码讲解视频
博客
在这里插入图片描述

核心参数

  • 变量state :没加锁state=0,加锁state=1
  • 加锁线程:CAS 设置state=1

加锁过程

  1. 尝试state的值由0变为1,如果发现state的值已经不为0了而是1,说明有线程加锁了
  2. 如果加锁失败,则放入等待队列中,等待其他线程释放锁

释放锁过程

  1. 将state递减为0,如果是可重入锁,例state=2,则需递减到0才会释放锁
  2. 将加锁线程set为null

可重入锁

判断加锁线程是否是当前线程(记录线程id)
ReentrantLcok、Synchronized
可以多次加锁lock和释放锁unlock

二. 源码剖析

源码讲解

1.核心参数

  1. int state
    ReentrantLcok、ReentrantReanWriteLock获取锁的方式是通过修改state变量
    CountDownLanch的计时器和Semaphore的信号量也是使用的state

  2. 双向链表 (未获取锁的线程)
    如果ReentrantLcok没拿到锁资源,则会将线程封装成Node存入,Node保存当前线程
    为什么这里使用双向链表?
    如果准备获取锁资源,需要出队,双向链表能够更好的修改头尾节点的指向
    双向链表初始化thread=null的节点,如果有新Node加入,(1)则优先修改Node自身的指向,指向为上一个节点(先操作私有变量,再操作共享变量volatile,保住原子性),(2 )让尾节点tail指向当前Node(3)让上一节点指向当前节点(存在多节点竞争,需要CAS加锁操作)
    走321,需要在多加一次锁
    在这里插入图片描述

  3. 单向链表(存wait挂起线程)
    waitSet
    存储ReentrantLcok使用await挂起的线程
    存储Synchronized 使用wait挂起的线程
    exq、EntryLis
    存储没获取锁资源的线程

2.ReentrantLock

abstract抽象类Sync继承了AQS
在这里插入图片描述
(1)ReentrantLock构造器

ReentrantLock默认无参构造非公平锁

	public ReentrantLock() {
        sync = new NonfairSync();
	}

有参构造,入参true公平锁new FairSync(),false非公平锁new NonFairSync()

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

(2)lock()
抽象类Syns有两个实现类NonfairSync和FairSync,所以ReentrantLock有非公平锁和公平锁两种实现,所以lock()方法也有两种

  • 非公平锁NonfairSync的lock()
    直接使用CAS,尝试将state由0改为1(CAS:同一时间多个线程执行,只有一个能成功),如果失败则走acquire(1)
	final void lock() {
      	if (compareAndSetState(0, 1))
           setExclusiveOwnerThread(Thread.currentThread());
        else
           acquire(1);
    }
  • 公平锁FairSync的lock()
    acquire(1)
	final void lock() {
            acquire(1);
	}

(3)acquire()方法 拿锁
acquire直接使用的AQS的方法,不区分公平锁、非公平锁实现

        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
  1. tryAcquire 再次拿锁,有公平锁、非公平锁两种实现
  2. addWaiter 没拿到锁,等待
  3. acquireQueued 挂起线程和后续被换线获取锁资源

acquire()的非公平锁实现nonfairTryAcquire(),尝试插队获取锁,tryLock()也使用的该方法

final boolean nonfairTryAcquire(int acquires) {
	//获取当前线程对象
     final Thread current = Thread.currentThread();
    //获取state
     int c = getState();
    //如果锁没用被有其他线程获取,则可以尝试获取
     if (c == 0) {
     	//非公平锁直接CAS尝试将state从0改为1
         if (compareAndSetState(0, acquires)) {
         	//成功拿到锁,并将当前线程设置为加锁线程
             setExclusiveOwnerThread(current);
             return true;
         }
     }
     //如果state!=1,判断当前线程是否为加锁线程
     else if (current == getExclusiveOwnerThread()) {
     	//如果是加锁线程,则锁重入,state+1
         int nextc = c + acquires;
         //防止int类型溢出,一般不会出现,可能先内存溢出
         if (nextc < 0) // overflow
             throw new Error("Maximum lock count exceeded");
         setState(nextc);
         return true;
     }
     return false;
 }

非阻塞锁tryLock()复用该方法nonfairTryAcquire()

public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

acquire()的公平锁实现tryAcquire(),排队获取锁

protected final boolean tryAcquire(int acquires) {
 final Thread current = Thread.currentThread();
 int c = getState();
 if (c == 0) {
 	//判断是否有其他线程在等待,如果没用则获取锁,如果需要排队则放弃
 	//如果当前线程排在第一位则抢锁(因为被唤醒线程也会执行tryAcquire方法,这里是为了考虑此情况)
     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;
}

(4)addWaiter(Node.EXCLUSIVE), arg)
将未获取到锁的线程,封装为Node,添加到双向链表

3.CAS unsafe.compareAndSwapInt

tryAcquire()方法中compareAndSetState()方法

if (compareAndSetState(0, acquires)) {
	setExclusiveOwnerThread(current);
	return true;
}

底层是native修饰的Unsafe工具类的compareAndSwapInt(),使用CAS指令修改(硬件层面支持)

  1. this:当前对象
  2. stateOffset:获取state内存偏移量
  3. expect:state的值
  4. update:需要修改的state的值,update=1
protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

获取当前AQS类下state属性在内存中的偏移量,也就是获取state属性的地址,因为地址是长整形所以用long接收

static final long stateOffset= unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("state"));

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

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

相关文章

C语言图书馆管理系统

以下是系统部分页面 以下是部分源码&#xff0c;需要源码的私信 #include<stdio.h> #include<stdlib.h> #include <string.h> #include<malloc.h>typedef struct {char student_name[100];char student_number[100];char password[100];int total_bor…

【一刷《剑指Offer》】面试题 46:求 1+2+...+n

力扣对应题目链接&#xff1a;LCR 189. 设计机械累加器 - 力扣&#xff08;LeetCode&#xff09; 牛客对应题目链接&#xff1a;求123...n_牛客题霸_牛客网 (nowcoder.com) 一、《剑指Offer》对应内容 二、分析题目 通常实现递归的时候我们都会利用条件判断语句来决定递归的出…

计算机体系结构||Cache性能分析(4)

实验4 Cache性能分析 4.1实验目的 &#xff08;1&#xff09;加深对Cache基本概念、基本组织结构以及工作原理的理解。 &#xff08;2&#xff09;掌握Cache容量、相关度、块大小对Cache性能的影响 &#xff08;3&#xff09;掌握降低Cache不命中率的各种方法以及它们对提高…

初识网络基础知识

关于网络的一些核心概念 局域网 局域网&#xff08;Local Area Network&#xff0c;简称LAN&#xff09;是一种计算机网络&#xff0c;覆盖的范围通常是相对较小的地理区域&#xff0c;比如一个办公室、一栋大楼或一个校园。 局域网的组成通常包括以下部分&#xff1a; 网络…

Docker Desktop Windows 目录介绍

D:\docker\DockerDesktopWSL\main\ext4.vhdx 和 D:\docker\DockerDesktopWSL\data\ext4.vhdx 是 Docker Desktop 在 Windows Subsystem for Linux&#xff08;WSL&#xff09;中使用的虚拟硬盘文件&#xff0c;它们有不同的用途和作用。 虚拟硬盘文件&#xff08;VHDX&#xf…

生成式多模态之AE DAE/MAE VAE VQ-VAE/VQ-VAE2

目录 1. AE2. DAE/MAE3. VAE4. VQ-VAE/VQ-VAE2生成式多模态发展主要历程 年份1月2月3月4月5月6月7月8月9月10月11月2020DETRDDPMDDIM、VisionTransformer2021CLIP、DALLESwin Transformer2022BLIPDALLE 2StableDiffusion、BEiT-3、Midjourney V32023BLIP2VisualChatGPT、Midjou…

linux内核中list的基本用法

内核链表 1 list_head 结构 为了使用链表机制&#xff0c;驱动程序需要包含<linux/types.h>头文件&#xff0c;该文件定义了如下结构体实现双向链&#xff1a; struct list_head {struct list_head *next, *prev; };2 链表的初始化 2.1 链表宏定义和初始化 可使用以…

AES算法概述

文章目录 一、AES 概述二、AES 加密算法的特点三、秘钥类型四、填充方式五、加密方式 一、AES 概述 二、AES 加密算法的特点 AES加密的数据块长度为128&#xff08;16字节&#xff09;&#xff0c;长度不足需要填充。密码长度可以为16,24,32字节。 三、秘钥类型 AES加密按秘…

【科大讯飞笔试题汇总】2024-07-20-科大讯飞秋招提前批(算法岗)-三语言题解(Cpp/Java/Python)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 ✨ 本系列打算持续跟新 秋招笔试题 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f4e7; 清隆这边最…

基于java+springboot+vue实现的企业OA管理系统(文末源码+Lw)131

基于SpringBootVue的实现的企业OA管理系统&#xff08;源码数据库万字Lun文流程图ER图结构图演示视频软件包&#xff09; 系统功能&#xff1a; 企业OA管理系统有管理员和用户。 管理员功能有个人中心&#xff0c;用户管理&#xff0c;公告信息管理&#xff0c;客户关系管理&…

【nnUNet V2系列】nnUNet V2在Ubuntu下安装调试篇

安装之前网上很多教程&#xff0c;很多是nnUNet V1的安装过程&#xff0c;有的V1和V2混在一起讲解&#xff0c;导致V1的转化指令用到V2中&#xff0c;产生不少误解。这篇是针对V2整理出来的安装过程&#xff0c;有什么不妥之处请指出会及时修改。 1. 创建虚拟环境 conda crea…

某4G区域终端有时驻留弱信号小区分析

这些区域其实是长时间处于连接态的电信卡4G终端更容易出现。 出现问题时都是band1 100频点下发了针对弱信号的1650频点的连接态A4测量事件配置&#xff08;其阈值为-106&#xff09;。而这个条件很容易满足&#xff0c;一旦下发就会切到band3 1650频点。 而1650频点虽然下发ban…

Visual Studio 2022美化

说明&#xff1a; VS版本&#xff1a;Visual Studio Community 2022 背景美化 【扩展】【管理扩展】搜索“ClaudiaIDE”&#xff0c;【下载】&#xff0c;安装完扩展要重启VS 在wallhaven下载壁纸图片作为文本编辑器区域背景图片 【工具】【选项】搜索ClaudiaIDE&#xff…

手机数据恢复技巧:适用于 Android 的恢复应用程序

发现自己意外删除了 Android 设备上的照片&#xff0c;这让人很痛苦。这些照片可能是值得纪念的文件&#xff0c;会让您想起一些难忘的回忆。删除它们后&#xff0c;您知道如何恢复它们。在这种情况下&#xff0c;您需要使用 Android 的照片恢复应用程序。 无论您需要直接从 A…

git跨库合并

1、背景 A为开发环境的代码仓库&#xff0c;B为生产环境的代码仓库。A和B之间不能通信。开发人员的本地电脑可以和A、B通信。 目的 上线时&#xff0c;需要将A代码合并B代码。 2、实现 2.1 添加远程仓库 2.1.1 代码方式 在B代码仓库中,将A添加为远程仓库。 git remote …

腾讯会议产品策划的成长之路:从万字文档到功能落地的实战经验

腾讯会议产品策划的成长之路&#xff1a;从万字文档到功能落地的实战经验 在腾讯会议的产品团队中&#xff0c;有这样一位产品策划&#xff0c;他以其出色的逻辑思维、全局观念以及扎实的执行力&#xff0c;在团队中发挥着举足轻重的作用。他就是林陪同&#xff0c;一个自称“会…

抽奖算法的设计与实现

更多内容欢迎访问我的个人博客网站&#xff1a;www.zpf0000.com 在数据库中准备好以下数据表 lottery表 sql代码解读复制代码 DROP TABLE IF EXISTS lottery; CREATE TABLE lottery (id int NOT NULL AUTO_INCREMENT,user_id int NOT NULL DEFAULT 0 COMMENT 发起抽奖用户ID,n…

【MySQL】:对库和表的基本操作方法

数据库使用的介绍 什么是SQL 学习数据库的使用——>基于 SQL编程语言 来对数据库进行操作 重点表述的是“需求”&#xff0c;期望得到什么结果。&#xff08;至于结果是如何得到的&#xff0c;并不关键&#xff0c;都是数据库服务器在背后做好了&#xff09; 重点表述的是…

DEGAS:将临床属性转移到细胞

DEGAS&#xff08;单细胞诊断证据量表&#xff0c;Diagnostic Evidence GAuge of Single cells&#xff09;是一种迁移学习框架&#xff0c;用于将疾病信息从患者转移到细胞。作者将这种可转移信息称为“印象-impressions”&#xff0c;它允许单细胞与疾病属性相关联&#xff0…

【Python】使用库 -- 详解

库就是别人已经写好了的代码&#xff0c;可以让我们直接拿来用。 一个编程语言能不能流行起来&#xff0c;一方面取决于语法是否简单方便容易学习&#xff0c;一方面取决于生态是否完备。所谓的 “生态” 指的就是语言是否有足够丰富的库&#xff0c;来应对各种各样的场景。在…