ReentrantLock详说

news/2024/5/15 18:22:29/文章来源:https://blog.csdn.net/jiayoubaobei2/article/details/129290640

目录

尝试加锁

如果加锁不成功

重点


尝试加锁

最外层lock方法

ReentrantLock.class

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

进来发现是个抽象方法 

abstract static class Sync extends AbstractQueuedSynchronizer

abstract void lock();

底下有两个实现类,一个实现的公平锁,另一个为非公平锁 

我们看一下公平锁吧

static final class FairSync extends Sync

这里面可以看到入参为1 

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

AbstractQueuedSynchronizer

public final void acquire(int arg) {if (!tryAcquire(arg) &&  // 这个是尝试去获取锁acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}

我们先去看怎么去获取锁的

protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException();}

列一下实现类

cas我理解就是通过线程的状态值和主内存中的状态值进行比较,如果相同则修改,如果不同则修改失败

protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread(); // 获取当前线程int c = getState(); // 获取state的值if (c == 0) { // 为0的话,说明还没有线程持有这把锁// 因为你是公平锁,不能上去就去抢,要先去排队if (!hasQueuedPredecessors() && // 判断是否需要排队compareAndSetState(0, acquires)) { // 如果不需要排队就尝试通过cas获取锁setExclusiveOwnerThread(current); //获取成功,那么该锁归这个线程所有return true;}}else if (current == getExclusiveOwnerThread()) { // 如果已经被加锁,但是还是这个线程想去获取锁,那么则进行+1操作,然后获取锁成功// 如果进入到这个if,只能只有线程进来,不存在并发,不需要通过cas进行修改int nextc = c + acquires;if (nextc < 0)throw new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}

看一下是否需要排队的方法

  • 就是看头节点和尾节点是否相同,相同肯定就为空,还有些其他判断
public final boolean hasQueuedPredecessors() {// The correctness of this depends on head being initialized// before tail and on head.next being accurate if the current// thread is first in queue.Node t = tail; // Read fields in reverse initialization orderNode h = head;Node s;return h != t &&((s = h.next) == null || s.thread != Thread.currentThread());}

如果加锁不成功

就需要尝试入队了,走 && 另一个方法了

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

这边新建了一个互斥模式的节点,有互斥就会有共享模式,共享模式后面再说

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

节点内比较重要的属性:

  • waitStatus:这个是代表节点的生命状态,新建的节点这个值就为0
    • signal,cancelled,condition,propagate,0
  • pre、next:这两个就是双向链表的前后指针
  • thread:所有需要阻塞的线程肯定需要保存在某个位置等待唤醒

因为刚开始前后节点都为空,addwaiter方法里面第一个判断是不走的,走enq方法

private Node enq(final Node node) {for (;;) {Node t = tail;if (t == null) { // Must initializeif (compareAndSetHead(new Node())) // 这里面就是把head指向新建的node// tail指向新建的nodetail = head;} else {node.prev = t;if (compareAndSetTail(t, node)) {t.next = node;return t;}}}}

这里面能看到,是个空循环,就是为了让拿不到锁的线程入队成功,开始走第一个逻辑就是为了初始化,设置一个空节点,反正这里面干了下图这个事,怎么干的我也不知道(我知道了,就是新建的节点作为head,tail = head,就是tail也是指向新建的节点)

看第二个判断就是初始化之后走这个判断,t就是新建的那个节点,初始化的节点,那个cas方法就是为了让tail指向node,最后把前驱节点进行返回,没什么用

 接着回到这个方法,里面入参那是包裹着这个线程的节点以及1        

public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}
acquireQueued方法
final boolean acquireQueued(final Node node, int arg) {boolean failed = true;try {boolean interrupted = false;for (;;) {final Node p = node.predecessor(); // 获得前驱节点if (p == head && tryAcquire(arg)) { // 如果前驱节点是头的话,就需要尝试去获取锁// 这里面就是获取锁成功,将该节点设为头节点setHead(node);p.next = null; // help GCfailed = false;return interrupted;}if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)cancelAcquire(node);}}

这里面可以看一下setHead方法,就是相当于将该节点置空变为前驱节点(就是之前初始化的空节点)

private void setHead(Node node) {head = node;node.thread = null;node.prev = null;}

如果不是头节点或者没有获取锁成功,那么就走下面的这个逻辑

shouldParkAfterFailedAcquire

需要去获取前驱节点的waitStatus的状态,如果为0,就需要变为-1,变为一个可以被唤醒的状态

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {int ws = pred.waitStatus;if (ws == Node.SIGNAL)/** This node has already set status asking a release* to signal it, so it can safely park.*/return true;if (ws > 0) {/** Predecessor was cancelled. Skip over predecessors and* indicate retry.*/do {node.prev = pred = pred.prev;} while (pred.waitStatus > 0);pred.next = node;} else {/** waitStatus must be 0 or PROPAGATE.  Indicate that we* need a signal, but don't park yet.  Caller will need to* retry to make sure it cannot acquire before parking.*/compareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false;}

然后被阻塞住

private final boolean parkAndCheckInterrupt() {LockSupport.park(this);return Thread.interrupted();
}

重点

acquireQueued(Node(currentThread), arg)
    节点阻塞之前还得再尝试一次获取锁:
    1,能够获取到,节点出队,并且把head往后挪一个节点,新的头结点就是当前节点
    2、不能获取到,阻塞等待被唤醒
        1.首先第1轮循环、修改head的状态,修改成sinal=-1标记处可以被唤醒.
        2.第2轮循环,阻塞线程,并且需要判断线程是否是有中断信号唤醒的!
        shouldParkAfterFailedAcquire(p, node)
    waitestate = 0 - > -1 head节点为什么改到-1,因为持有锁的线程T0在释放锁的时候,得判断head节点的waitestate是否!=0,如果!=0成立,会再把waitstate = -1->0,要想唤醒排队的第一个线程T1,T1被唤醒再接着走循环,去抢锁,可能会再失败(在非公平锁场景下),此时可能有线程T3持有了锁!T1可能再次被阻塞,head的节点状态需要再一次经历两轮循环:waitState = 0 -> -1

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

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

相关文章

Zookeeper3.5.7版本——Zookeeper的概述、工作机制、特点、数据结构及应用场景

目录一、Zookeeper的概述二、Zookeeper的工作机制三、Zookeeper的特点四、Zookeeper的数据结构五、Zookeeper的应用场景5.1、统一命名服务5.2、统一配置管理5.3、统一集群管理5.4、服务器动态上下线5.5、软负载均衡一、Zookeeper的概述 Zookeeper 是一个开源的分布式的&#x…

39万字完整版智能矿山项目建设整体解决方案

本资料来源网络&#xff0c;仅做知识分享&#xff0c;请勿商用。完整资料领取见文末&#xff0c;部分资料内容&#xff1a; 1.1 总体技术要求 1.1.1 核心业务架构 智能矿山业务架构是在统一的标准与规范及安全运维保障体系下&#xff0c;按分层设计模式&#xff0c;分为设备层、…

【基于机器学习的推荐系统项目实战-1】初识推荐系统

本文目录一、为什么我们需要推荐系统&#xff1f;二、推荐系统的发展阶段三、推荐系统模型四、通用推荐系统框架4.1 数据生产4.2 数据存储4.3 算法召回4.4 结果排序4.5 结果应用4.6 新浪微博的框架开源结构图五、推荐常用特征5.1 用户特征5.2 物品特征六、推荐常用算法七、结果…

【自动化测试】一位自动化测试工程师居然不会封装框架?神秘自动化测试框架......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 自动化测试框架 自…

山寨APP频出?安全工程师和黑灰产在较量

在山寨这个领域&#xff0c;没有人比黑灰产更懂模仿。 据安全从业者介绍&#xff0c;一般而言&#xff0c;对于成熟的山寨开发者来说&#xff0c;几天时间内就可以做出一套前端框架。服务器、源代码、域名、服务商这些内容的创建&#xff0c;通过网上租赁的方式就可以解决。 比…

小红书“复刻”微信,微信“内造”小红书

配图来自Canva可画 随着互联网增长红利逐渐见顶&#xff0c;各大互联网平台对流量的争夺变得愈发激烈。而为了寻找新的业务可能性&#xff0c;各家都在不遗余力地拓宽自身边界。在此背景下&#xff0c;目前最为“吸睛”和“吸金”的社交、电商、种草、短视频等领域&#xff0c…

Qt音视频开发19-vlc内核各种事件通知

一、前言 对于使用第三方的sdk库做开发&#xff0c;除了基本的操作函数接口外&#xff0c;还希望通过事件机制拿到消息通知&#xff0c;比如当前播放进度、音量值变化、静音变化、文件长度、播放结束等&#xff0c;有了这些才是完整的播放功能&#xff0c;在vlc中要拿到各种事…

【vulhub漏洞复现】CVE-2018-2894 Weblogic任意文件上传漏洞

一、漏洞详情影响版本weblogic 10.3.6.0、weblogic 12.1.3.0、weblogic 12.2.1.2、weblogic 12.2.1.3WebLogic是美国Oracle公司出品的一个application server&#xff0c;确切的说是一个基于JAVAEE架构的中间件&#xff0c;WebLogic是用于开发、集成、部署和管理大型分布式Web应…

快速吃透π型滤波电路-LC-RC滤波器

π型滤波器简介 π型滤波器包括两个电容器和一个电感器&#xff0c;它的输入和输出都呈低阻抗。π型滤波有RC和LC两种&#xff0c; 在输出电流不大的情况下用RC&#xff0c;R的取值不能太大&#xff0c;一般几个至几十欧姆&#xff0c;其优点是成本低。其缺点是电阻要消耗一些…

计算机组成原理4小时速成6:输入输出系统,io设备与cpu的链接方式,控制方式,io设备,io接口,并行串行总线

计算机组成原理4小时速成6&#xff1a;输入输出系统&#xff0c;io设备与cpu的链接方式&#xff0c;控制方式&#xff0c;io设备&#xff0c;io接口&#xff0c;并行串行总线 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c…

操作系统——11.线程概念和多线程模型

这篇文章我们来讲一下操作系统中的线程概念和多线程模型 目录 1.概述 2.线程 2.1问题提出 2.2线程概念 2.3带来的变化 2.4线程的属性 2.5线程的实现方式 2.5.1用户级线程 2.5.2内核级线程 2.5.3相互组合 2.6多线程模型 2.6.1多对一模型 2.6.2一对一模型 2.6.3多…

linux代码库生成-make示例

1、add.c代码实现加法运算&#xff1a; int add(int a,int b) { return ab; } 2、头文件add.h #ifndef ADD_H #define ADD_H int add(int a,int b); #endif 3、CMakeLists.txt编写 cmake_minimum_required(VERSION 2.6) set(CMAKE_C_FLAGS -m32) project(test_add) include_dir…

骨传导耳机推荐,列举几款骨传导主流的耳机分享

随着科技的发展&#xff0c;耳机也发生了巨大的变化&#xff0c;从最初的传统入耳式耳机到骨传导耳机&#xff0c;越来越多的人开始使用这种听音乐的方式。它可以带给我们更好的体验。骨传导耳机顾名思义就是一种类似骨传导发声原理的无线耳机。这篇文章就是介绍目前市面上比较…

【快速理解和上手】逻辑回归logistic regression (无原理,只有公式化步骤)

逻辑回归 Logistic regression 目的&#xff1a;解决二分类问题 数学效果&#xff1a;根据数据(x⃗,y)(\vec{x}, y)(x,y) &#xff08;其中y为0或1&#xff09;&#xff0c;拟合一条曲线&#xff0c;x轴表示特征&#xff0c;y轴表示一个概率&#xff0c;即这个输入 xxx 对应着…

《OpenGL宝典》--统一变量

统一变量 [layout (location 0)] uniform float f 1.0f;若设置layout&#xff0c;则不需要使用glGetUniformLocation来获取统一变量的位置 使用glUniform*传递值&#xff0c;glUniformMatrix*()设置矩阵统一变量。 glUseProgram(myShader); glUniform1f(0,45.2f);//0为loc…

香港双重牌照、准入安排和禁止事项等重要制度已明确 20多万字的《虚拟资产咨询文件》以证雄心

前不久&#xff0c;香港证监会就加密货币交易发布的《有关适用于获证券及期货事务监察委员会发牌的虚拟资产交易平台营运者的建议监管规定的咨询文件》&#xff08;以下简称《咨询文件》&#xff09;&#xff0c;并如期就有关监管虚拟资产交易平台的建议展开咨询&#xff0c;以…

PT100铂电阻温度传感器

PT100温度传感器又叫做铂热电阻。     热电阻是中低温区&#xfe61;常用的一种温度检测器。它的主要特点是测量精度高&#xff0c;性能稳定。其中铂热电阻的测量精确度是&#xfe61;高的&#xff0c;它不仅广泛应用于工业测温&#xff0c;而且被制成标准的基准仪。金属热…

储、AI、车?特斯拉宏图第三篇章即将揭晓

美东时间周三(3月1日&#xff09;下午4点&#xff0c;特斯拉将在得州总部举办备受瞩目的“投资者日”活动&#xff0c;马斯克之前所提出的“宏图第三篇章&#xff08;Master Plan Part 3)”的悬念也即将揭晓。马斯克宏图计划到底是什么&#xff1f;关于第三篇我们可以有那些期待…

Transformer及其子孙后代

三大灵魂模型 Transformer attention is all you need&#xff0c;现在已经是money is all you need时代了&#xff08;x 首先介绍自注意力机制 Atention⁡(Q,K,V)softmax⁡(QKTdk)V\operatorname{Atention}(Q,K,V)\operatorname{softmax}(\dfrac{QK^T}{\sqrt{d_k}})VAtenti…

目前医疗器械数据库有哪些?最推荐哪些?

在英特网发达的今天&#xff0c;医疗器械信息查询应该是一件便捷的事情&#xff0c;但不知道大家有没有遇到过这种类似情况&#xff0c;就是在查询医疗器械信息时&#xff0c;如果通过百度去检索&#xff0c;查到的结果往往会不尽人意&#xff0c;比如信息陈旧、太分散、来源不…