3、显示锁和AQS

article/2025/9/23 6:09:11

3、显示锁和AQS

原子操作CAS
atom(不可分割)

什么是原子操作?如何实现原子操作?
synchronized 基于阻塞的锁机制 可以实现,但会引发以下问题

1、被阻塞的线程优先级很高

2、拿到锁的线程一直不释放锁怎么办?

3、大量的竞争,消耗CPU,同时带来死锁或者其他安全问题。

CAS的原理
利用了现代处理器都支持的CAS指令,循环这个指令,直到成功为止;

CAS(Compare And Swap),指令级别保证这是一个原子操作;

三个运算符: 一个内存地址 V 、 一个期望值 A 、 一个新值 B

基本思路:如果地址V上的值和我期望的值A相等,就给地址V赋给新值B,如果不是,不做任何操作;

循环(死循环,自旋)里面不断的进行CAS操作;

CAS的问题
ABA问题
A - 》 B - 》 A

加版本号控制

A1 - 》 B2 - 》 A3

开销问题
CAS操作长期不成功,CPU不断循环

只能保证一个共享变量的原子操作
多个变量封装成一个对象使用

jdk相关原子操作类的使用;
AtomicMarkableReference , boolean 有没有动过

AtomicStampedReference , 动过几次

显示锁
Lock 接口和 synchronized 比较
synchronized 代码简洁,

lock 获取锁可以被中断,超时获取锁,尝试获取锁

不是特别必要 尽量使用 synchronized

可重入锁 ReentrantLock 、所谓的公平锁和非公平锁
如果在时间上,先对锁获取的请求 一定先被满足,就是公平锁

不满足 则是非公平锁;

非公平锁的效率一般来讲更高;

ReentrantLock(boolean fair) 可指定是否公平;

ReadWriteLock 、 ReentrantReadWriteLock
ReentrantLock 和 synchronized 都是排他锁

读写锁:同一时刻允许多个读线程同时访问;但写线程访问的时候所有的读和写都被阻塞;

读多写少的情况 读写锁性能提升非常明显 但是什么业务场景会用到呢?

什么是AQS ? 学习它的必要性
了解LockSupport工具
作用:
阻塞一个线程
唤醒一个线程

构建同步组件的基础工具
part 开头的方法
unpark(Thread thread)

AQS使用方式和其中的设计模式
继承,模版方法设计模式

了解其中的方法
模版方法

独占式获取锁

void acquire(int arg)
void acquireInterruptibly(int arg)
boolean tryAcquire(int arg)

独占式释放锁
boolean release(int arg)

共享式获取锁
void acquireShared(int arg)
void acquireSharedInterruptibly(int arg)
boolean tryAcquireSharedNanos(int arg, long nanosTimeout)

共享式释放锁
boolean releaseShared(int arg)

需要子类覆盖的流程方法
独占式获取
boolean tryAcquire(int arg)

独占式释放
boolean tryRelease(int arg)

共享式获取
int tryAcquireShared(int arg)

共享式释放
boolean tryReleaseShared(int arg)

这个同步器是否处于独占模式
boolean isHeldExclusively()

同步状态 volatile int state
获取同步状态 final int getState()
设置同步状态 final void setState(int newState) 无法保证原子性

设置同步状态 compareAndSetState(int expect, int update) 使用CAS设置 、保证设置状态的原子性

AQS中的数据结构-节点和同步队列
AbstractQueuedSynchronizer 深入分析
先进先出

class Node
waitStatus:
CANCELLED : 线程等待超时或者中断了,需要从队列里移走;
SIGNAL : 后续的节点处于等待状态,当前节点,通知后面的节点去运行;
CONDITION: 当前节点处于等待队列;
PROPAGATE: 共享,表示状态要往后面的节点传播;
0 表示当前节点处于初始状态;

Node prev; 上一个线程
Node next; 下一个线程

独占式状态获取与释放

在这里插入图片描述
独占式获取锁 源代码解析:

public final void acquire(int arg) {//获取锁成功就返回,否则 加入等候队列if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}private Node addWaiter(Node mode) {//当前线程包装成 NodeNode node = new Node(Thread.currentThread(), mode);// Try the fast path of enq; backup to full enq on failureNode pred = tail;//   快速加入尾节点;加入成功returnif (pred != null) {//设置当前线程节点 的头节点为 当前队列的尾节点node.prev = pred;// 如果当前队列的尾节点是 当前线程节点的头节点  // 就把队列尾节点设置为当前线程节点if (compareAndSetTail(pred, node)) {pred.next = node;return node;}}//  否则  自旋加enq(node);return node;}private Node enq(final Node node) {for (;;) {Node t = tail;if (t == null) { // Must initializeif (compareAndSetHead(new Node()))tail = head;} else {node.prev = t;if (compareAndSetTail(t, node)) {t.next = node;return t;}}}}final boolean acquireQueued(final Node node, int arg) {boolean failed = true;try {boolean interrupted = false;for (;;) {// 临时变量p 设置为当前节点的头节点final Node p = node.predecessor();// 如果当前节点的头节点是  head 节点 并且尝试获取锁成功//则 设置当前节点为头节点 。回收这一节点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);}unlock源代码解析}

unlock源代码解析:

public final boolean release(int arg) {// 尝试释放锁成功if (tryRelease(arg)) {// 变量h  为当前头节点Node h = head;// 如果h 不为 null  并且  等候状态不为0 if (h != null && h.waitStatus != 0)//  唤醒头节点unparkSuccessor(h);return true;}return false;}protected boolean tryRelease(int arg) {throw new UnsupportedOperationException();}private void unparkSuccessor(Node node) {/** If status is negative (i.e., possibly needing signal) try* to clear in anticipation of signalling.  It is OK if this* fails or if status is changed by waiting thread.*/int ws = node.waitStatus;if (ws < 0)compareAndSetWaitStatus(node, ws, 0);/** Thread to unpark is held in successor, which is normally* just the next node.  But if cancelled or apparently null,* traverse backwards from tail to find the actual* non-cancelled successor.*/Node s = node.next;if (s == null || s.waitStatus > 0) {s = null;for (Node t = tail; t != null && t != node; t = t.prev)if (t.waitStatus <= 0)s = t;}if (s != null)LockSupport.unpark(s.thread);}

共享式获取锁:

public final void acquireShared(int arg) {// 获取锁失败返回 < 0 if (tryAcquireShared(arg) < 0)// 获取锁失败doAcquireShared(arg);}private void doAcquireShared(int arg) {final Node node = addWaiter(Node.SHARED);boolean failed = true;try {boolean interrupted = false;for (;;) {// 死循环 设置 临时变量p 为当前节点的上一节点final Node p = node.predecessor();// 如果是头节点if (p == head) {// 尝试获取锁int r = tryAcquireShared(arg);//获取成功if (r >= 0) {//设置当前节点为头节点 并往下传播 r 个节点setHeadAndPropagate(node, r);// 回收当前节点p.next = null; // help GCif (interrupted)selfInterrupt();failed = false;return;}}//阻塞if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)cancelAcquire(node);}}private void setHeadAndPropagate(Node node, int propagate) {Node h = head; // Record old head for check belowsetHead(node);/** Try to signal next queued node if:*   Propagation was indicated by caller,*     or was recorded (as h.waitStatus either before*     or after setHead) by a previous operation*     (note: this uses sign-check of waitStatus because*      PROPAGATE status may transition to SIGNAL.)* and*   The next node is waiting in shared mode,*     or we don't know, because it appears null** The conservatism in both of these checks may cause* unnecessary wake-ups, but only when there are multiple* racing acquires/releases, so most need signals now or soon* anyway.*/if (propagate > 0 || h == null || h.waitStatus < 0 ||(h = head) == null || h.waitStatus < 0) {Node s = node.next;if (s == null || s.isShared())doReleaseShared();}}

独占式超时获取锁:

public final boolean tryAcquireNanos(int arg, long nanosTimeout)throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();return tryAcquire(arg) ||doAcquireNanos(arg, nanosTimeout);}private boolean doAcquireNanos(int arg, long nanosTimeout)throws InterruptedException {if (nanosTimeout <= 0L)return false;//记录时间final long deadline = System.nanoTime() + nanosTimeout;final Node node = addWaiter(Node.EXCLUSIVE);boolean failed = true;try {for (;;) {final Node p = node.predecessor();if (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // help GCfailed = false;return true;}//等待超时获取nanosTimeout = deadline - System.nanoTime();if (nanosTimeout <= 0L)return false;if (shouldParkAfterFailedAcquire(p, node) &&nanosTimeout > spinForTimeoutThreshold)LockSupport.parkNanos(this, nanosTimeout);if (Thread.interrupted())throw new InterruptedException();}} finally {if (failed)cancelAcquire(node);}}

Condition
在这里插入图片描述

节点在队列之间的移动

在这里插入图片描述
公平锁和非公平锁的区别;
获取锁时多了这么一个判断

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());}

非公平锁效率为什么高
公平锁等候队列里挨个Node 从唤醒到 启动 的时间是无法消除的。


http://chatgpt.dhexx.cn/article/F7ncfu8i.shtml

相关文章

LaTex绘制跨行跨列的三线表

有时表格某单元格需要横跨几列&#xff0c;我们可以使用 \multicolumn 命令&#xff0c; 同时使用 booktabs 宏包的 \cmidrule 命令来画横跨几列的横线。它们的语 法如下&#xff1a; \begin{table }[ htbp]\centering \begin{tabular }{ lll}\toprule & \multicolumn{2}{…

Springer latex期刊模板使用的坑

1.之前都是用这个编译&#xff0c;Springer 期刊不是这个&#xff0c;是pdflatex 解决方案&#xff1a;可以上面选项换成pdflatex也可以加这命令\documentclass[pdflatex,sn-basic]{sn-jnl}&#xff08;注意documentclass只能出现一次&#xff0c;所以pdflatex可以加在原有的d…

LaTeX--5--一个文档的基本结构/导言区/标题_作者_日期

本期的主要内容有&#xff1a; 一个 LaTeX 文档的基本结构导言区常用排版命令标题、作者与日期字体与字号特殊字符的输入一些常用排版效果 另外我们还会穿插介绍一些 LaTeX 的基本概念&#xff0c;在文中加粗表示并附上英文。 1. 一个 LaTeX 文档的基本结构 承接上一期&…

Latex表格大小调整常见问题汇总

列宽调整 若要调整每列的宽度到指定数值&#xff0c;可以将代码中的 \begin{tabular}{c|ccc}改为 \begin{tabular}{p{xxx}|ccc}其中花括号里面填写宽度数值&#xff0c;如5pt&#xff0c;10mm等。如果需要该列居中&#xff0c;可以写作 \begin{tabular}{p{xxx}<{\center…

Elsevier模板LaTex中编译时出现“! Undefined control sequence.”

问题描述(以下内容仅是我遇到的问题&#xff0c;不一定具有普适性) 使用overleaf模板库里的ElsevierLaTex模板上传到系统后&#xff0c;系统并没有正确的生成pdf&#xff0c;而是生成了报错信息。 后来改用了Elsevier官网的latex模板&#xff0c;但是上传之后还是生成报错信息…

latax中插入表格

下面这篇博客写的很好&#xff0c;这里转载了过来。另外需要补充几点&#xff1a; 1.绘制表格的简单方法 latax绘制复杂表非常的麻烦&#xff0c;故一般采取其他方法。 可以使用Excel2LaTeX插件 https://www.latexstudio.net/archives/6992.html &#xff08;需要安装ms off…

Latex的常见问题及解决方案

制作斜线表头 \usepackage{diagbox} \begin{tabular}{|l|ccc|} \hline \diagbox{Time}{Room}{Day} & Mon & Tue & Wed \\ \hline Morning & used & used & \\ Afternoon & & used & used \\ \hline \end{tabular} 效果如下: 空格表示 …

LaTex学习笔记之命令使用

LaTex学习笔记之命令使用 概要vscode跳转写作环境宏包的使用命令的使用常用命令章节层次列表环境字体字号行内和行间公式分式、根式、矩阵表格的使用插图参考文献的插入 概要 本文章是针对已安装好Tex环境&#xff0c;并已安装好编辑器。这里使用TeXstudio进行编辑。 vscode跳…

LaTeX心得分享之插入表格(下)

接上篇~今天我们主要说&#xff1a; 单元格的合并 单元格的拆分 一、单元格的合并 1、\multicolumn命令 。通常用于合并一行种几个不同列的单元格&#xff0c;适合排版跨列的标头。其基本语法格式&#xff1a; \multicolumn{项数}{新列格式}{名称} 项数&#xff1a;表格的…

LaTeX制表命令使用教程(简要例子+清晰代码)(论文排版)

1.基本格式 \documentclass{article}\begin{document}\begin{tabular}{cc}%一个c表示有一列&#xff0c;格式为居中显示(center) (1,1)&(1,2)\\%第一行第一列和第二列 中间用&连接 (2,1)&(2,2)\\%第二行第一列和第二列 中间用&连接 \end{tabular}\end{docum…

语义分割 FastFcn ~ JPU单元

Paper: FastFCN: Rethinking Dilated Convolution in the Backbone for Semantic Segmentation 贡献&#xff1a; 提出JPU替代 扩张卷积计算时间和内存消耗减少3倍且有更好的表现在一些公开数据集上 start of the art 文章以resnet101作为backbone, deeplab系列在resnet上去掉…

JDNI

JNDI是为了一个最最核心的问题&#xff1a;是为了解耦&#xff0c;是为了开发出更加可维护、可扩展的系统JNDI和JDBC起的作用类似&#xff1a;JDBC&#xff08;Java Data Base Connectivity,java数据库连接&#xff09;是一种用于执行SQL语句的Java API&#xff0c;可以为多种关…

JGit

JGit 参考&#xff1a;https://yonge812.iteye.com/blog/1687480 概念&#xff1a; 就是用java代码实现git的命令行操作 JGit API&#xff1a; https://download.eclipse.org/jgit/site/5.2.1.201812262042-r/apidocs/index.html 打开git仓库 Git gitGit.open&#xff…

java pom_Jpom

软件简介 Jpom是一款简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件。 你为什么需要Jpom Java 项目在实际部署运维&#xff0c;通用的方法是登录服务器上传新的项目包&#xff0c;执行相应命令管理&#xff0c;如果管理多个项目则重复操作上述步骤 此方法不足的是…

我对FastFCN的理解

FastFCN:重新思考膨胀卷积在语义分割主干网络中的作用 扩张卷积膨胀卷积空洞卷积 一、前言 1.解决的问题 时下的语义分割模型通常在主干网络中使用扩张卷积来获得高分辨率的特征图&#xff0c;但是这样做会增加计算复杂度和内存占用。 该论文提出了一种新型的联合上采样模块…

基于LPRNet的车牌识别算法移植与测试

课程全程将在[SOPHGO&#xff08;算能&#xff09;云平台上进行。 本次课程将介绍: &#xff08;1&#xff09;SOPHGO&#xff08;算能&#xff09;云平台环境搭建 &#xff08;2&#xff09;LPRNet算法 &#xff08;3&#xff09;通过BMNNSDK 2.7.0进行LPRNet模型转换和量…

Javajr

第一章 计算机基础 1.1 计算机的结构体系 通过CPU的控制器将输入设备中数据读取到CPU中的存储器中,然后通过运算器将存储器的数据进行运算,最后再有控制器将运算器的结果显示到输出设备中。 这个结构我们称之为冯诺依曼体系结构。 第二章 Java基础环境搭建 2.1Java技术体…

深度学习(9):FastFCN论文翻译与学习

FastFCN: Rethinking Dilated Convolution in the Backbone for Semantic FastFCN:重新思考膨胀卷积在语义分割主干网络中的作用 注&#xff1a;部分插图近几天补上&#xff0c;赶其他ddl去了 注&#xff1a;作者的理论分析我觉得主要集中在3.2&#xff0c;大家可以注意一下 在…

JPDA

Transferability versus Discriminability:Joint Probability Distribution Adaptation (JPDA) 最新的一篇迁移学习&#xff08;传统方法&#xff09; 读后感。。。 一、introduction 传统的迁移学习可以分成&#xff1a; 基于参数的&#xff1a;需要目标域中有带有label的样…

【第五期论文复现赛-语义分割】FastFCN

【论文复现赛】FastFCN: Rethinking Dilated Convolution in the Backbone for Semantic Segmentation 本文提出了一个新的联合上采样模块JPU(Joint Pyramid Upsampling)&#xff0c;将提取高分辨率特征图的任务映射成一个联合上采样问题。JPU模块可应用于各种语义分割模型中&…