当前位置: 首页 > news >正文

秋招Java后端开发冲刺——并发篇2(JMM与锁机制)

本文对Java的内存管理模型、volatile关键字和锁机制进行详细阐述,包括synchronized关键字、Lock接口及其实现类ReentrantLock、AQS等的实现原理和常见方法。

一、JMM(Java内存模型)

1. 介绍
JMM定义了共享内存多线程程序读写操作的行为规范,通过这些规则来规范对内存的读写操作从而保证指令的正确性。
2. 模型
在这里插入图片描述

  • JMM把内存分为两块,一块是私有线程的工作区域(工作内存),一块是所有线程的共享区域(主内存)
  • 线程跟线程之间是相互隔离,线程跟线程交互需要通过主内存

二、Volatile关键字

1. 介绍

  • Volatile关键字主要用于修饰成员变量(实例变量或类变量),使得对该变量的读写操作直接与主内存交互,而不是通过线程本地的工作内存进行缓存。
  • Volatile关键字提供了一种轻量级的同步机制,用于保证多线程环境下数据的可见性(但不能保证数据的原子性,Synchronized关键字既保证可见性,又保证原子性)

2. Volatile禁止指令重排
(1)指令重排

指令重排是一种编译器和处理器优化技术,它改变指令执行的顺序,以提高性能。在单线程环境下,这种优化不会影响程序的正确性。但是在多线程环境下,指令重排可能导致意想不到的结果,破坏程序的正确性。

(2)Volatile如何禁止指令重排

  • 通过在读写 volatile 变量时插入内存屏障(Memory Barriers)来实现
    • 写屏障(Write Barrier):确保在写 volatile 变量之前的所有写操作在内存中完成。
    • 读屏障(Read Barrier):确保在读 volatile 变量之后的所有读操作在内存中完成
  • volatile关键字一般加在写变量最后一个,读变量第一个。
class Example {private volatile boolean flag = false;private int a = 0;public void writer() {a = 1;           // 1flag = true;     // 2}public void reader() {if (flag) {      // 3int i = a;   // 4}}
}

上述示例中:

  • 在writer方法中,写入a = 1(1)操作不能被重排序到写入flag = true(2)之后。因为flag是volatile,在写flag的时候会插入一个StoreStore屏障,确保在flag被写入之前,所有之前的写操作都完成了。
  • 在reader方法中,读取flag(3)操作不能被重排序到读取a(4)之前。因为flag是volatile,在读flag的时候会插入一个LoadLoad屏障,确保在flag被读取之后,所有之前的读操作都完成了。

三、锁

1. 悲观锁和乐观锁
(1)悲观锁:认为访问共享资源时一定会发生线程安全问题,因此在访问时必须加锁。
(2)乐观锁:认为访问共享资源时不一定会发生线程安全问题,因此不会加锁,当提交修改的时候去验证对应的资源(也就是数据)是否被其它线程修改即可。
(3)二者区别

特性悲观锁乐观锁
锁定机制操作数据前先加锁,其他线程无法操作操作数据时不加锁,提交时检查冲突
使用场景适用于写多读少,冲突概率高的场景适用于读多写少,冲突概率低的场景
性能由于频繁加锁和释放锁,性能较低不加锁,性能较高
实现方式使用数据库锁机制(如行锁、表锁)使用版本号或时间戳进行冲突检测
数据一致性一致性强,通过锁机制防止并发修改一致性依赖于冲突检测和重试机制
并发性并发性差,锁定会阻塞其他线程并发性好,线程可以同时进行操作
死锁风险存在死锁风险不存在死锁风险
典型应用行锁、表锁、页锁和意向锁(数据库)、ReentrantLock等版本控制系统、CAS(Compare and Swap)操作

2. synchronized 关键字
(1)介绍
synchronized 关键字是一种对象锁,采用互斥的方式让同一时刻至多只有一个线程能持有对象锁。
(2)底层实现原理
① Monitor(对象监视器)

  • Monitor(监视器)是一种同步机制,用于管理多线程之间的互斥访问和并发控制。Monitor是JVM提供的,但是是基于c++实现的。
  • Monitor的结构
    • Owner:存储当前获取锁的线程的,只能有一个线程可以获取
    • EntryList:关联没有抢到锁的线程,处于Blocked状态的线程
    • WaitSet:关联调用了wait方法的线程,处于Waiting状态的线程

在这里插入图片描述
② synchronized关键字实现原理
线程获得锁需要使用对象(锁)关联monitor监视器,即获得monitor的持有权。

(3)使用方法

  • 同步方法:当一个线程执行同步方法时,它会自动获得该方法所属对象的锁。其他线程在获得该锁之前无法执行该方法。
public class SynchronizedExample {private int count = 0;public synchronized void increment() {count++;}public synchronized int getCount() {return count;}
}
  • 同步块:同步块用于在一个方法内同步部分代码,提供了比同步方法更细粒度的控制。
public class SynchronizedBlockExample {private final Object lock = new Object();private int count = 0;public void increment() {synchronized (lock) {count++;}}public int getCount() {synchronized (lock) {return count;}}
}
  • 静态同步方法:静态同步方法锁定的是类对象,对于所有该类的实例都有效。
public class StaticSynchronizedExample {private static int count = 0;public static synchronized void increment() {count++;}public static synchronized int getCount() {return count;}
}

3. synchronized锁升级

Monitor实现的锁属于重量级锁,因为monitor是使用c++实现的,里面涉及到了用户态和内核态的切换、进程的上下文切换,成本较高,性能比较低。在JDK 1.6引入了两种新型锁机制:偏向锁轻量级锁

(1)对象的内存结构
在这里插入图片描述
:填充为8的整数倍是方便字节对齐提高程序运行效率 ,而且对于压缩指针也更加的方便。

(2)轻量级锁
在无竞争的情况下,轻量级锁使用CAS操作来实现锁的获取和释放,提高运行效率。
① 加锁流程

  • 在线程栈中创建一个Lock Record,将其obj字段指向锁对象。
  • 通过CAS指令将Lock Record的地址存储在对象头的mark word中,如果对象处于无锁状态则修改成功,代表该线程获得了轻量级锁。
  • 如果是当前线程已经持有该锁了,代表这是一次锁重入。设置Lock Record第一部分为null,起到了一个重入计数器的作用。(仍需CAS操作)
  • 如果CAS修改失败,说明发生了竞争,需要膨胀为重量级锁。

② 解锁过程

  • 遍历线程栈,找到所有obj字段等于当前锁对象的Lock Record。
  • 如果Lock Record的Mark Word为null,代表这是一次重入,将obj设置为null后continue。
  • 如果Lock Record的 Mark Word不为null,则利用CAS指令将对象头的mark word恢复成为无锁状态。如果失败则膨胀为重量级锁。

(3)偏向锁
与轻量级锁不同的是,偏向锁只第一次获得锁时使用 CAS 将线程 ID 设置到对象的 Mark Word 头,之后该线程再获取锁时,只需要判断锁对象mark word中是否是自己的线程id即可。

:轻量级锁和偏向锁都是在没有竞争的情况下使用,一旦发生了竞争会立刻升级为重量级锁。

4. Lock接口:ReentrantLock

Lock接口提供了一种比传统的同步块和方法更灵活的锁机制。Lock 接口有多种实现,其中最常用的是 ReentrantLock。

(1)Lock接口常见实现类

实现类描述
ReentrantLock可重入的互斥锁,具有与使用 synchronized 方法和语句相同的基本行为和语义,但功能更强大。
ReentrantReadWriteLock.ReadLock读锁,可被多个读线程同时持有,只要没有写线程持有写锁。
ReentrantReadWriteLock.WriteLock写锁,独占锁,只有写线程可以持有,读线程和其他写线程都被阻塞。
StampedLock一种新的读写锁实现,支持乐观读锁和写锁的互斥锁操作,提高并发性和性能。
LockSupport提供了基本的线程阻塞原语,用于创建锁和其他同步工具。

(2)ReentrantLock
① ReentrantLock是一个可重入且独占式的锁,还支持轮询、超时、中断、公平锁和非公平锁等。
② 特点

  • 可重入性:一个线程可以多次获得该锁,而不会导致死锁。这意味着同一个线程可以进入锁保护的代码块多次,必须确保在离开代码块时相应的次数解锁。
  • 公平性:可以选择公平锁或者非公平锁。公平锁按照线程请求的顺序获取锁,非公平锁则没有这种顺序,可能导致某些线程长时间等待。
  • 灵活性:提供了比 synchronized 块更灵活的锁获取方式,如可中断的锁获取、超时的锁获取。

③ 实现原理

  • ReentrantLock使用CAS+AQS来实现,使用用一个整数state表示锁的状态,使用一个先进先出的队列(CLH)管理被阻塞的线程。
  • 当线程需要获得锁时,使用CAS操作修改锁的状态,修改成功则获得锁;修改失败则加入CLH队列管理。

④ 常用方法

方法描述
lock()获取锁。如果锁不可用,则当前线程将被禁用以进行线程调度,并处于休眠状态,直到获得锁。
unlock()释放锁。
tryLock()尝试获取锁。如果锁在调用时未被另一个线程持有,则获取锁并立即返回 true;否则返回 false
tryLock(long time, TimeUnit unit)尝试在给定的等待时间内获取锁。如果锁在给定时间内可用,则获取锁并返回 true;否则返回 false
lockInterruptibly()如果当前线程未被中断,则获取锁。如果锁不可用,则当前线程出于线程调度目的将被禁用并处于休眠状态,直到发生以下两种情况之一:锁由当前线程获得;或者其他某个线程中断当前线程。
newCondition()返回一个绑定到此 Lock 实例的新 Condition 实例。

5. synchronized与ReentrantLock的比较

特性synchronizedReentrantLock
锁类型隐式锁显式锁
获取锁的方式自动需要显式调用 lock() 方法
释放锁的方式自动需要显式调用 unlock() 方法
可重入性
公平锁支持是(通过构造函数可以指定)
条件变量支持是(通过 newCondition() 方法)
获取锁中断是(通过 lockInterruptibly() 方法)
超时锁获取是(通过 tryLock(long time, TimeUnit unit) 方法)
性能开销较低(JVM 内部优化)较高
锁的范围代码块或方法灵活,可锁代码块
代码复杂度较高
推荐使用场景简单的同步需求复杂的同步需求,如需要公平锁、超时等

6. AQS(Abstract Queued Synchronizer,抽象队列同步器)
(1)介绍
AQS是Java 并发包 (java.util.concurrent.locks) 中的一个核心框架,是构建各种锁和同步组件的基础框架。
(2)实现原理

  • AQS 使用一个 int 类型的变量 state 表示同步状态。
  • AQS 内部维护了一个 FIFO 队列,用于管理获取锁失败的线程。线程在争夺锁失败后会被封装成队列节点(Node)并加入到等待队列中。
  • 队列中每个节点包含线程引用和状态标志

(3)常用方法

方法描述
acquire(int arg)独占模式获取锁,如果获取失败则进入等待队列。
release(int arg)独占模式释放锁,如果释放成功则唤醒等待队列中的下一个节点。
acquireShared(int arg)共享模式获取锁,如果获取失败则进入等待队列。
releaseShared(int arg)共享模式释放锁,如果释放成功则唤醒等待队列中的下一个节点。
addWaiter(Node mode)将当前线程封装成节点并加入等待队列的尾部。
unparkSuccessor(Node node)唤醒等待队列中的下一个节点。

(4)常见实现类

实现类描述
ReentrantLock可重入独占锁。允许线程重复获取同一把锁。
ReentrantReadWriteLock可重入读写锁。支持多个线程同时读取数据,但写操作是互斥的。
Semaphore信号量。控制同时访问特定资源的线程数。
CountDownLatch倒计时闩。一个或多个线程等待其他线程完成操作后再继续执行。
CyclicBarrier循环栅栏。一组线程相互等待,达到同步点后再继续执行。
Phaser阶段器。管理多个参与者线程的同步,支持动态注册和注销。
StampedLock读写锁的改进版本,支持乐观读取和写锁。
Condition条件变量。与锁配合使用,允许线程等待某个条件变为真。

相关文章:

秋招Java后端开发冲刺——并发篇2(JMM与锁机制)

本文对Java的内存管理模型、volatile关键字和锁机制进行详细阐述,包括synchronized关键字、Lock接口及其实现类ReentrantLock、AQS等的实现原理和常见方法。 一、JMM(Java内存模型) 1. 介绍 JMM定义了共享内存中多线程程序读写操作的行为规…...

记录一次Chrome浏览器自动排序ajax请求的JSON数据问题

文章目录 1.前言2. 为什么会这样?3.如何解决? 1.前言 作者作为新人入职的第一天,mentor给了一个维护公司运营平台的小需求,具体需求是根据运营平台的某个管理模块所展示记录的某些字段对展示记录做排序。 第一步: myb…...

【嵌入式——FreeRTOS】任务

【嵌入式——FreeRTOS】任务 任务创建和删除动态方式创建任务静态方式创建任务 删除任务任务切换调度器任务切换流程 任务挂起任务恢复相关API函数 任务创建和删除 动态方式创建任务 任务的任务控制块以及任务的栈空间所需的内存,均由freeRTOS从freeRTOS管理的堆中…...

网关,路由器,交换机

一、网关 (Gateway) 是一种设备,用于连接不同网络,能够转发数据包并翻译协议,允许不同类型的网络通信。网关通常工作在OSI模型的应用层或传输层,提供连接和路由服务。 应用场景例子: 在企业网络中,网关可…...

sublime 3 背景和字体颜色修改

sublime 4 突然抽风,每次打开都显示 “plugin_host-3.3 has exited unexpectedly, some plugin functionality won’t be available until Sublime Text has been restarted” 一直没调好,所以我退回到sublime 3了。下载好了软件没问题,但是一…...

leetcode 403周赛 包含所有1的最小矩形面积||「暴力」

3197. 包含所有 1 的最小矩形面积 II 题目描述: 给你一个二维 二进制 数组 grid。你需要找到 3 个 不重叠、面积 非零 、边在水平方向和竖直方向上的矩形,并且满足 grid 中所有的 1 都在这些矩形的内部。 返回这些矩形面积之和的 最小 可能值。 注意…...

Stable Diffusion web UI 插件

2024.7.3更新,持续更新中 如果需要在linux上自己安装sd,参考:stable diffusion linux安装 插件复制到 /stable-diffusion-webui/extensions 目录下,然后重新启动sd即可 一、插件安装方法 每种插件的安装方法可能略有不同&#xf…...

深度学习中的反向传播算法的原理

深度学习中的反向传播算法的原理,以及如何计算梯度 反向传播算法(Backpropagation)是深度学习中最核心的优化技术之一,用于训练神经网络。它基于链式法则,通过从输出层逆向计算误差并逐层传递到输入层来更新模型参数&…...

身处奇瑞看三星:既“开卷“又“起火“,却更难受了

三星"起火" 这几天奇瑞的事情,让大家破防了,纷纷表示国内的就业市场环境普遍恶劣。 那我们转个眼,看看海外企业的情况。 最近一周,三星频频登上新闻,颇有"起火"之势。 在刚步入下半年的 7 月 1 日…...

系统架构设计师教程(清华第2版)<第1章 绪论>解读

系统架构设计师教程 第一章 绪论 1.1 系统架构概述1.1.1 系统架构的定义及发展历程1.1.2 软件架构的常用分类及建模方法1.1.3 软件架构的应用场景1.1.4 软件架构的发展未来1.2 系统架构设计师概述1.2.1 架构设计师的定义、职责和任务1.2.2 架构设计师应具备的专业素质1.3 如何成…...

Vue + Element UI + JSEncrypt实现简单登录页面

安装依赖 npm install jsencrypt --save局部引入 import JSEncrypt from jsencrypt/bin/jsencrypt;登录页面index.vue <template><div class"loginbody"><div class"logindata"><div class"logintext"><h2>Wel…...

从“关注流”到“时间线”,搜狐给内容加信任价值

文 | 螳螂观察 作者 | 易不二 在近日第十六季搜狐新闻马拉松活动中&#xff0c;搜狐新闻APP的“时间线”功能备受瞩目。不仅开幕式现场竖了一块“左手时间线&#xff0c;右手关注流”的路牌&#xff0c;张朝阳也着重强调了“时间线”产品的互动方式&#xff1a;“关注是基础&…...

vscode的一些使用问题

vscode使用技巧 1、快捷键&#xff08;1&#xff09;打开命令面板&#xff08;2&#xff09;注释&#xff08;3&#xff09;删除行&#xff08;4&#xff09;上下移动光标&#xff08;5&#xff09;光标回退&#xff08;6&#xff09;复制行&#xff08;7&#xff09;插入空白行…...

爬虫-网页基础

HTML 基本语法 HTML&#xff1a;Hyper Text Markup Language, 超文本标记语言&#xff0c;是计算机语言的一种&#xff0c;由元素构成。 p元素 <p>Web 真好玩&#xff01;</p> 由三大部分组成 开始标签&#xff1a;一对尖括号中间包裹这元素名称元素内容&#x…...

保存huggingface缓存中AI模型(从本地加载AI模型数据)

在github下拉项目后,首次运行时会下拉一堆模型数据&#xff0c;默认是保存在缓存的&#xff0c;如果你的系统盘空间快满的时候就会被系统清理掉&#xff0c;每次运行又重新下拉一次&#xff0c;特别麻烦。 默认下载的缓存路径如下&#xff1a;C:\Users\用户名\.cache\huggingf…...

wps的xlsm和xltm和xlam格式的文件各有什么区别

文章目录 一、前言二、WPS表格文件格式介绍1. .xlsm 文件格式2. .xltm 文件格式3. .xlam 文件格式 三、总结 一、前言 本文将详细介绍WPS表格中三种常见的文件格式&#xff1a;.xlsm、.xltm、和.xlam&#xff0c;并提供通俗易懂的解释和示例&#xff0c;帮助用户理解它们的区别…...

软件性能测试有哪几种测试方法?专业性能测试报告出具

软件性能测试是指对软件系统在特定负载条件下的性能进行评估和验证的过程&#xff0c;目的是确保软件在正常使用的情况下能够满足用户的要求&#xff0c;并在稳定的性能水平下运行&#xff0c;在软件开发过程中起到了至关重要的作用&#xff0c;可以确保软件产品的质量和可靠性…...

JavaScript语言简介与实战应用:从零开始的编程之旅

JavaScript&#xff0c;一种轻量级的、解释型的、面向对象的脚本语言&#xff0c;自1995年由Netscape公司的Brendan Eich设计以来&#xff0c;迅速成为了Web开发中不可或缺的一部分。它不仅能够为静态网页添加动态效果&#xff0c;还能实现客户端与服务器的交互&#xff0c;如今…...

如何理解synchronized锁升级

在Java中&#xff0c;synchronized 关键字是实现线程同步的一种方式&#xff0c;它涉及到锁的升级和释放的过程。理解synchronized 锁的升级可以分为三个阶段&#xff1a;无锁状态、偏向锁状态和轻量级锁状态。 无锁状态&#xff1a; 当对象被创建时&#xff0c;默认处于无锁状…...

js【最佳实践】遍历数组的八种方法(含数组遍历 API 的对比)for,forEach,for of,map,filter,reduce,every,some

遍历方法返回值使用场景备注副作用for 循环——遍历数组通用可以改变原数组forEach 循环——遍历数组ES5 新增&#xff0c;不支持中断和异步可以改变原数组for of 循环——遍历数组ES6 新增可以改变原数组map格式化后的数组格式化数组的API不会改变原数组filter过滤后的数组过滤…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

comfyui 工作流中 图生视频 如何增加视频的长度到5秒

comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗&#xff1f; 在ComfyUI中实现图生视频并延长到5秒&#xff0c;需要结合多个扩展和技巧。以下是完整解决方案&#xff1a; 核心工作流配置&#xff08;24fps下5秒120帧&#xff09; #mermaid-svg-yP…...

前端高频面试题2:浏览器/计算机网络

本专栏相关链接 前端高频面试题1&#xff1a;HTML/CSS 前端高频面试题2&#xff1a;浏览器/计算机网络 前端高频面试题3&#xff1a;JavaScript 1.什么是强缓存、协商缓存&#xff1f; 强缓存&#xff1a; 当浏览器请求资源时&#xff0c;首先检查本地缓存是否命中。如果命…...

Monorepo架构: Nx Cloud 扩展能力与缓存加速

借助 Nx Cloud 实现项目协同与加速构建 1 &#xff09; 缓存工作原理分析 在了解了本地缓存和远程缓存之后&#xff0c;我们来探究缓存是如何工作的。以计算文件的哈希串为例&#xff0c;若后续运行任务时文件哈希串未变&#xff0c;系统会直接使用对应的输出和制品文件。 2 …...