java锁常识

在这里插入图片描述

AQS框架

AQS(AbstractQueuedSynchronizer)是 Java 中用于构建锁和同步器的基础框架。它提供了一种实现同步器的方式,使得开发者可以基于 AQS 构建各种类型的同步工具,如独占锁、共享锁、信号量等。

AQS 主要基于 FIFO 队列(即先进先出队列)的方式来管理线程之间的竞争和等待。它的核心思想是将等待线程封装成一个 FIFO 的队列,当某个线程需要获取锁而锁已经被其他线程占用时,该线程会被封装成一个节点(Node)并加入到等待队列中,然后被阻塞。当锁被释放时,AQS 会从队列中选择一个线程唤醒,使其尝试获取锁。

AQS 的设计允许开发者通过实现不同的方法来定制同步器的行为,其中最重要的方法包括 tryAcquire() 和 tryRelease(),它们分别用于尝试获取和释放锁。通过重写这些方法,开发者可以创建各种不同类型的锁和同步器。

AQS

class AQS{
private volatile int state;
//借助 FIFO 队列管理线程
private static class Node {
volatile Node prev;
volatile Node next;
Thread thread;

    Node(Thread thread) {
        this.thread = thread;
    }
}

private volatile Node head;
private volatile Node tail;

}
它使用了一个简单的双向链表来存储等待获取锁的线程,通过一个条件变量 Condition 来实现线程的等待和唤醒。当线程尝试获取锁时,它会加入到链表的尾部,如果发现自己不是头结点,则进入等待状态;当释放锁时,它会从链表头部移除,并唤醒下一个等待线程。

乐观锁和悲观锁

在这里插入图片描述

// ------------------------- 悲观锁的调用方式 -------------------------
// synchronized
public synchronized void testMethod() {
	// 操作同步资源
}
// ReentrantLock
private ReentrantLock lock = new ReentrantLock(); // 需要保证多个线程使用的是同一个锁
public void modifyPublicResources() {
	lock.lock();
	// 操作同步资源
	lock.unlock();
}

// ------------------------- 乐观锁的调用方式 -------------------------
private AtomicInteger atomicInteger = new AtomicInteger();  // 需要保证多个线程使用的是同一个AtomicInteger
atomicInteger.incrementAndGet(); //执行自增1(通过CAS)

atomicInteger是CAS,通过CPU的cmpxchg指令,去比较寄存器中的 A 和 内存中的值 V。如果相等,就把要写入的新值 B 存入内存中。如果不相等,就将内存值 V 赋值给寄存器中的值 A。然后通过Java代码中的while循环再次调用cmpxchg指令进行重试,直到设置成功为止。

自旋

在这里插入图片描述
AtomicInteger中调用unsafe进行自增操作的源码中的do-while循环就是一个自旋操作

无锁

无锁没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。

无锁的特点就是修改操作在循环内进行,线程会不断的尝试修改共享资源。如果没有冲突就修改成功并退出,否则就会继续循环尝试。如果有多个线程修改同一个值,必定会有一个线程能修改成功,而其他修改失败的线程会不断重试直到修改成功。上面我们介绍的CAS原理及应用即是无锁的实现。无锁无法全面代替有锁,但无锁在某些场合下的性能是非常高的。

偏向锁

偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价。

在大多数情况下,锁总是由同一线程多次获得,不存在多线程竞争,所以出现了偏向锁。其目标就是在只有一个线程执行同步代码块时能够提高性能。

当一个线程访问同步代码块并获取锁时,会在Mark Word里存储锁偏向的线程ID。在线程进入和退出同步块时不再通过CAS操作来加锁和解锁,而是检测Mark Word里是否存储着指向当前线程的偏向锁。引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径,因为轻量级锁的获取及释放依赖多次CAS原子指令,而偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令即可。

偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程不会主动释放偏向锁。偏向锁的撤销,需要等待全局安全点(在这个时间点上没有字节码正在执行),它会首先暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态。撤销偏向锁后恢复到无锁(标志位为“01”)或轻量级锁(标志位为“00”)的状态。

偏向锁在JDK 6及以后的JVM里是默认启用的。可以通过JVM参数关闭偏向锁:-XX:-UseBiasedLocking=false,关闭之后程序默认会进入轻量级锁状态。

轻量级锁

是指当锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能。

在代码进入同步块的时候,如果同步对象锁状态为无锁状态(锁标志位为“01”状态,是否为偏向锁为“0”),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝,然后拷贝对象头中的Mark Word复制到锁记录中。

拷贝成功后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,并将Lock Record里的owner指针指向对象的Mark Word。

如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位设置为“00”,表示此对象处于轻量级锁定状态。

如果轻量级锁的更新操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行,否则说明多个线程竞争锁。

若当前只有一个等待线程,则该线程通过自旋进行等待。但是当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁升级为重量级锁。

重量级锁

升级为重量级锁时,锁标志的状态值变为“10”,此时Mark Word中存储的是指向重量级锁的指针,此时等待锁的线程都会进入阻塞状态。

整体的锁状态升级流程如下:

无锁

无锁没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。

无锁的特点就是修改操作在循环内进行,线程会不断的尝试修改共享资源。如果没有冲突就修改成功并退出,否则就会继续循环尝试。如果有多个线程修改同一个值,必定会有一个线程能修改成功,而其他修改失败的线程会不断重试直到修改成功。上面我们介绍的CAS原理及应用即是无锁的实现。无锁无法全面代替有锁,但无锁在某些场合下的性能是非常高的。

偏向锁

偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价。

在大多数情况下,锁总是由同一线程多次获得,不存在多线程竞争,所以出现了偏向锁。其目标就是在只有一个线程执行同步代码块时能够提高性能。

当一个线程访问同步代码块并获取锁时,会在Mark Word里存储锁偏向的线程ID。在线程进入和退出同步块时不再通过CAS操作来加锁和解锁,而是检测Mark Word里是否存储着指向当前线程的偏向锁。引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径,因为轻量级锁的获取及释放依赖多次CAS原子指令,而偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令即可。

偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程不会主动释放偏向锁。偏向锁的撤销,需要等待全局安全点(在这个时间点上没有字节码正在执行),它会首先暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态。撤销偏向锁后恢复到无锁(标志位为“01”)或轻量级锁(标志位为“00”)的状态。

偏向锁在JDK 6及以后的JVM里是默认启用的。可以通过JVM参数关闭偏向锁:-XX:-UseBiasedLocking=false,关闭之后程序默认会进入轻量级锁状态。

轻量级锁

是指当锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能。

在代码进入同步块的时候,如果同步对象锁状态为无锁状态(锁标志位为“01”状态,是否为偏向锁为“0”),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝,然后拷贝对象头中的Mark Word复制到锁记录中。

拷贝成功后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,并将Lock Record里的owner指针指向对象的Mark Word。

如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位设置为“00”,表示此对象处于轻量级锁定状态。

如果轻量级锁的更新操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行,否则说明多个线程竞争锁。

若当前只有一个等待线程,则该线程通过自旋进行等待。但是当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁升级为重量级锁。

重量级锁

升级为重量级锁时,锁标志的状态值变为“10”,此时Mark Word中存储的是指向重量级锁的指针,此时等待锁的线程都会进入阻塞状态。

整体的锁状态升级流程如下:
在这里插入图片描述

可重入锁 VS 非可重入锁

Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。
一个类中synchronized修饰的方法a和方法b,在方法a中可以递归调用方法a或者b,都不会阻塞死锁。
ReentrantLock和NonReentrantLock都继承父类AQS,其父类AQS中维护了一个同步状态status来计数重入次数,status初始值为0。
在这里插入图片描述

读写锁

在最开始提及AQS的时候我们也提到了state字段(int类型,32位),该字段用来描述有多少线程获持有锁。

在独享锁中这个值通常是0或者1(如果是重入锁的话state值就是重入的次数),在共享锁中state就是持有锁的数量。但是在ReentrantReadWriteLock中有读、写两把锁,所以需要在一个整型变量state上分别描述读锁和写锁的数量(或者也可以叫状态)。于是将state变量“按位切割”切分成了两个部分,高16位表示读锁状态(读锁个数),低16位表示写锁状态(写锁个数)。
在这里插入图片描述

在这里插入图片描述

在ReentrantLock虽然有公平锁和非公平锁两种,但是它们添加的都是独享锁。
在这里插入图片描述

原文:https://tech.meituan.com/2018/11/15/java-lock.html

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

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

相关文章

Qt 集成OSG

Qt 你好 | 专注于Qt的技术分享平台 一&#xff0c;新建一个 QOsgWidget 类&#xff0c;继承自osgQOpenGLWidget #ifndef QOSGWIDGET_H #define QOSGWIDGET_H#include <QObject> #include <osgViewer/Viewer> #include <osgQOpenGL/osgQOpenGLWidget> class…

一种基于PET和ICT的双功能荧光探针,用于同时识别活细胞中的Cys和H2S

文章解读 文章设计了一种易于合成的双功能荧光探针NJB&#xff0c;通过明显的颜色和荧光变化用于对Cys和H2S的双位点响应&#xff0c;。在检测过程中&#xff0c;发生了光诱导电子转移 (photoinduced electron transfer, PET)和分子内电荷转移 (ICT)过程&#xff0c;NJB的颜色和…

ffmpeg支持MP3编码的方法

目录 现象 解决办法 如果有编译包没有链接上的情况 现象 解决办法 在ffmpeg安装包目录下 &#xff0c;通过./configure --list-encoders 和 ./configure --list-decoders 命令可以看到&#xff0c;ffmpeg只支持mp3解码&#xff0c;但是不支持mp3编码。 上网查寻后发现&…

SpringBoot之@Conditional衍生条件装配详解

文章目录 ☃️前言☃️简介☃️示例❄️❄️ConditionalOnProperty❄️❄️ConditionalOnClass❄️❄️ConditionalOnBean❄️❄️自定义条件 ☃️SpringBoot源码中使用☃️总结 欢迎来到 请回答1024 的博客 &#x1f353;&#x1f353;&#x1f353;欢迎来到 请回答1024的博客…

【圆桌论坛】个人作为嘉宾参与问答环节的总结,Create 2024百度AI开发者大会之AI智能体开发与应用论坛

目录 ⭐前言⭐讨论话题✨本质和价值✨端侧部署✨应用商业模式✨商业模式 ⭐主题总结⭐有趣分享 ⭐前言 首先&#xff0c;非常荣幸和开心作为开发者和创业者代表参加百度Create AI大会分论坛圆桌论坛的问答环节。 在分论坛活动开始前&#xff0c;参加了文心智能体平台&#xff…

JS 删除数组元素( 5种方法 )

No.内容链接1Openlayers 【入门教程】 - 【源代码示例300】 2Leaflet 【入门教程】 - 【源代码图文示例 150】 3Cesium 【入门教程】 - 【源代码图文示例200】 4MapboxGL【入门教程】 - 【源代码图文示例150】 5前端就业宝典 【面试题详细答案 1000】 文章目录 一、五种…

vue3中web前端JS动画案例(四)侧边栏横幅效果-右下角广告-淘宝案例

myJsAnimation.js, 这里使用了上次封装的动画方法&#xff0c;并进行了改造 /*** 动画的函数* dom 当前对象* JSON 传入元素对象的属性 {"width": 300, "opacity": 50}* * -------------------- 多物体运动&#xff0c;同时运动 ---传入JSON-------------*…

爬虫零基础学习,第一天,安装环境,requests库常用命令的讲解

Python爬虫 爬虫学习思路 URL内容获取,requests的基本常用语法 import requests # 先向目标网站发送请求 url = "http://www.baidu.com" r

【TensorFlow深度学习】人工智能绪论与深度学习前瞻

人工智能绪论与深度学习前瞻 【引言】人工智能的起源与发展机器学习与深度学习的关系深度学习的兴衰与复兴深度学习的特点与前瞻 【引言】 在信息技术蓬勃发展的今天&#xff0c;人工智能已成为推动科技革新和社会进步的关键驱动力。从最初的计算机辅助人类处理信息&#xff0…

UDP文件传输工具之UDP传输的优点和缺点

在当今快节奏的网络通信时代&#xff0c;UDP以其独特的优势&#xff0c;在众多应用场景中扮演着关键角色。本文将深入探讨UDP的优缺点及其应用场景&#xff0c;并重点介绍镭速软件如何通过技术创新&#xff0c;显著提升UDP传输的效率和可靠性。 UDP传输的优点 UDP的显著优势在…

从奇门WMS-A到金蝶云星空通过接口配置打通数据

从奇门WMS-A到金蝶云星空通过接口配置打通数据 接入系统&#xff1a;奇门WMS-A 用于菜鸟的仓库&#xff08;使用其他支持奇门的仓库同理&#xff09;&#xff0c;故而希望能和仓库的wms系统打通&#xff0c;这样我们采购收货&#xff0c;采购入库&#xff0c;销售出库&#xff…

HTML随机点名程序

案例要求 1.点击点名按钮&#xff0c;名字界面随机显示&#xff0c;按钮文字由点名变为停止 2.再次点击点名按钮&#xff0c;显示当前被点名学生姓名&#xff0c;按钮文字由停止变为点名 案例源码 <!DOCTYPE html> <html lang"en"> <head> <m…

流量反作弊算法简介

参考&#xff1a;流量反作弊算法实践 1. 背景 阅读记录阿里流量作弊的风控文章。甄别阿里妈妈逾千亿商业流量中作弊 与 低质量的部分&#xff0c;保护广告主和平台的利益是风控团队的核心工作之一。 2. 广告风控流程 广告主投放内容与风控团队、下游业务团队的简易交互流程如…

ArrayList与顺序表(2)

前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&#x1…

JavaScript中的map()方法详解

1. map() 的返回值是一个新的数组&#xff0c;新数组中的元素为 “原数组调用函数处理过后的值” 2. 简单使用&#xff1a;遍历整个数组&#xff0c;将大于4的元素乘以2 const array [2, 3, 4, 4, 5, 6]console.log("array",array) const map array.map(x > {…

nfs网络存储配置

准备&#xff1a;yum install rpcbind yum install nfs-server 一台服务器&#xff1a;192.168.220.131 一台客户端&#xff1a;192.168.220.220 服务器&#xff1a; 先启动rpcbind服务&#xff1a;systemctl restart rpcbind 在启动…

B站下行CDN架构的探索与应用

本期作者 背景介绍 B站的下行CDN旧架构如下图所示&#xff0c;可以看到边缘CDN节点与中心调度服务有紧密协作&#xff0c;简单说是先由调度服务进行流量调度(负责均衡的调度到每个网关组件节点&#xff09;&#xff0c;再由回源组件进行集群内的回源收敛&#xff0c;最终到对应…

Rust-01 Hello Rust 10分钟上手编写第一个Rust程序 背景介绍 发展历史 环境配置 升级打怪的必经之路

背景介绍 Rust 是一种多范式、通用的编程语言&#xff0c;强调性能、类型安全和并发性。它通过一个称为“借用检查器”的机制在编译时追踪所有引用的对象生命周期&#xff0c;以强制实现内存安全&#xff0c;即确保所有引用都指向有效的内存&#xff0c;而不需要垃圾收集器。 …

Qwen1.5微调

引子 由于工作上需要&#xff0c;一直在用Qwen做大模型推理&#xff0c;有个再训练的需求&#xff0c;特此琢磨下Qwen的训练。OK&#xff0c;我们开始吧。 一、安装环境 查看显卡驱动版本 根据官网推荐 OK&#xff0c;docker在手&#xff0c;天下我有。 docker pull qwenll…

白酒:馥郁香型白酒的香气特点与生产工艺

云仓酒庄的豪迈白酒介绍到&#xff0c;馥郁香型白酒以其与众不同的香气特点和杰出的生产工艺赢得了消费者的青睐。馥郁香型白酒以其香气浓郁、口感醇厚、回味悠长而著称。下面云仓酒庄豪迈白酒将深入探讨馥郁香型白酒的香气特点与生产工艺。 云仓酒庄豪迈白酒讲诉&#xff0c;馥…