Java锁 从乐观锁和悲观锁开始讲 面试复盘

目录

面试复盘

Java 中的锁 大全

悲观锁

专业解释

自我理解

乐观锁

专业解释

自我理解

悲观锁的调用

乐观锁的调用

synchronized和 ReentrantLock的区别

相同点

区别

详细对比

总结


面试复盘

Java 中的锁 大全

悲观锁

专业解释

适合写操作多的场景

先加锁可以保证写操作时数据正确

显式的锁定之后再操作同步资源

自我理解

悲观锁认为自己使用数据的时候一定有其他线程来修改数据

因此在获取数据的时候会选择先加锁

确保数据不会被别的线程修改

synchronized 和 Lock 的实现类都是悲观锁

乐观锁

专业解释

适合读操作多的场景

不加锁的特点能够使其读操作的性能大幅提升

乐观锁则能直接去操作同步资源

是一种无锁算法

自我理解

乐观锁认为自己在操作数据的时候不会有别的线程修改数据,所以不会加锁,所以他只会在自己操作数据的时候检查是否有其他线程修改更新的这个数据。

如果乐观锁去操作数据,这个数据没有更新的话。当前线程会直接将修改成功的数据写入,如果数据已经被其他线程更新了。要通过不同的实现方式进行不同操作。乐观锁在Java中是通过使用无锁编程来实现的,常用的是CAS算法。

Java原子类中的递增操作就是用CAS 自旋完成的

悲观锁的调用

import java.util.concurrent.locks.ReentrantLock;public class OptiPessLockDemo {// 悲观锁的调用方式public  synchronized void m1(){// 加锁后的业务逻辑...}// 保证多个线程使用的是同一个lock对象的前提下ReentrantLock lock=new ReentrantLock();public void m2(){lock.lock();try {// 操作同步资源}finally {lock.unlock();}}// 两个都是悲观锁
}

假设在任何时候都可能发生冲突,因此,线程必须显式地获取锁以确保数据一致性和线程安全,直到它执行完毕并释放锁。

对于 synchronized,在方法级别加锁时,锁是针对该对象的,保证同一时刻只有一个线程能够执行该方法。因此,当一个线程在执行 m1() 方法时,其他线程不能同时执行同一个对象上的 m1() 方法。

  • synchronized 关键字的锁粒度是方法级别的,锁住的是整个方法。在方法执行期间,其他线程不能进入这个方法。
  • synchronized 实现的是隐式加锁和解锁,不需要显式地调用 lock()unlock()
  • ReentrantLock 是 Java 中的一个显式锁,它提供了比 synchronized 更细粒度的锁控制和更多功能。
  • ReentrantLock 通过 lock.lock() 显式地加锁,调用 lock.unlock() 来释放锁。通常,这两个操作会放在 try...finally 代码块中,以确保即使出现异常,锁也能得到释放。

乐观锁的调用

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;public class OptiPessLockDemo2 {// 乐观锁的调用方式private AtomicInteger atomicInteger = new AtomicInteger();atomicInteger.incrementAndGet();public static void main(String[] args) {int oldValue = atomicInteger.get();int newValue = oldValue + 1;// 如果值没有被修改,原子性操作成功return atomicInteger.compareAndSet(oldValue, newValue);}
}

这种方式在判断和更新之间,确保了只有一个线程能够成功更新值,其他线程则会重试或失败,从而保证了乐观锁的行为。

总之,乐观锁的核心是希望在操作时不加锁,直到最后验证冲突发生与否。如果有冲突,则可以通过重试、回滚等方式处理。

synchronizedReentrantLock的区别

synchronizedReentrantLock 都用于实现 悲观锁(Pessimistic Locking),即在多线程环境中对共享资源进行加锁,以保证线程安全。尽管两者实现的目标相同,它们在使用方式、灵活性、性能等方面有一些不同。下面我们来详细分析它们的 相同点区别

相同点

  1. 线程安全
    • 两者都能确保在多线程环境下,访问共享资源时,只有一个线程能够持有锁,防止多个线程同时修改共享数据,避免数据不一致。
  1. 互斥性
    • 两者都保证了同一时刻只有一个线程能够执行被保护的代码块或方法。其他线程必须等待当前线程释放锁后才能执行。
  1. 支持重入性
    • synchronizedReentrantLock 都是可重入的(Reentrant),即同一线程可以多次获取同一个锁,而不会发生死锁。
  1. 作用范围
    • 两者都可以应用于同步方法或同步代码块,保护共享资源。

区别

特性

synchronized

ReentrantLock

实现方式

隐式加锁,Java 编译器在编译时自动处理。

显式加锁,需要手动调用 lock()

unlock()

锁粒度

锁定整个方法或代码块,无法精确控制。

可以精确控制锁定的范围,允许更灵活的锁定操作。

性能

相对较低,特别是在高并发环境下,由于 JVM 的锁优化不足,可能导致性能瓶颈。

在高并发时,ReentrantLock

性能优于 synchronized

,尤其在锁竞争激烈时。

中断支持

不支持中断,线程获取锁时无法响应中断。

支持中断,可以使用 lockInterruptibly()

来在等待锁时响应中断。

公平性

非公平锁,线程不一定按照请求的顺序获取锁。

可以选择公平锁或非公平锁。使用构造函数 new ReentrantLock(true)

来创建公平锁。

死锁避免

需要小心死锁问题,synchronized

无法避免死锁。

通过 ReentrantLock

提供的 tryLock()

方法和超时机制可以更灵活地避免死锁。

锁释放机制

锁由 JVM 自动管理,方法执行完后自动释放。

必须手动调用 unlock()

释放锁,通常与 try...finally

语句配合使用。

可重入性

支持,可同一线程多次获取同一锁。

支持,可同一线程多次获取同一锁。

性能监控

无法直接获取锁的状态。

可以通过 getHoldCount()

获取当前线程持有锁的次数,进行监控。

锁升级

不支持锁的升级(无法从轻量级锁升级为重量级锁)。

可以通过锁的竞争情况动态升级为不同的锁类型(如偏向锁、轻量锁、重量锁)。


详细对比

  1. 锁的获取与释放
    • synchronized隐式加锁,锁的获取和释放是自动完成的。线程在执行被 synchronized 修饰的代码时,会自动获取该对象的锁,方法或代码块执行完后自动释放锁。
    • ReentrantLock显式加锁,必须手动调用 lock() 获取锁,必须手动调用 unlock() 释放锁。通常会配合 try...finally 语句使用,以保证在执行完业务逻辑后无论是否发生异常都能够释放锁。
  1. 中断响应
    • synchronized 在获取锁时无法响应中断。如果线程在等待锁的过程中被中断,它会继续等待,直到获取到锁。
    • ReentrantLock 提供了 lockInterruptibly() 方法,它允许在等待锁的过程中响应中断。线程在等待锁时,如果被中断,能够及时退出等待。
  1. 公平性
    • synchronized非公平锁,即任何线程在请求锁时,获取锁的顺序并不一定按照请求的顺序。可能先被调用的线程后获得锁,后调用的线程反而先获取到锁。
    • ReentrantLock 可以通过构造函数指定是否为 公平锁。如果设置为公平锁,锁会按照请求顺序分配给线程,先请求的线程会先获得锁。公平锁相对会增加一些性能开销,因此通常默认是非公平锁。
  1. 性能
    • 在低并发环境下,synchronized 的性能和 ReentrantLock 差不多,但在高并发环境下,ReentrantLock 会因为其灵活性和优化而表现得更好。
    • synchronized 锁的实现较为简单,但在高并发下可能存在 锁竞争 的问题,导致性能瓶颈。
    • ReentrantLock锁竞争 情况下提供了更多的优化方式,如通过自旋锁、CAS 等机制来减少线程的阻塞,从而提高性能。
  1. 死锁避免
    • synchronized 并没有提供直接的 API 来避免死锁。需要开发者自己通过代码设计来避免死锁问题。
    • ReentrantLock 提供了 tryLock() 方法,可以尝试获取锁,如果获取失败,可以选择放弃或重试。这种机制可以帮助开发者避免死锁或减少锁的等待时间。
  1. 锁的升级和监控
    • synchronized 锁的状态无法直接监控,只能通过 JVM 的内部调试工具进行查看。
    • ReentrantLock 提供了诸如 getHoldCount() 等方法来获取当前线程持有锁的次数,此外,还可以通过 getQueueLength() 来获取等待锁的线程数量,便于性能监控和分析。

总结

  • synchronized:适用于简单的同步需求,易于使用,代码简洁,但不够灵活,性能在高并发场景下可能有所下降。
  • ReentrantLock:功能更强大,提供了更多灵活性,如公平锁、中断响应、尝试加锁等,适用于复杂的多线程应用。但需要手动管理锁的获取与释放,使用上稍微复杂一些。

如果你的应用需求比较简单,且对性能要求不高,使用 synchronized 就足够了。如果你需要更多的控制、灵活性和对高并发场景的优化,ReentrantLock 会是更好的选择。

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

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

相关文章

使用nginx搭建通用的图片代理服务器,支持http/https/重定向式图片地址

从http切换至https 许多不同ip的图片地址需要统一进行代理 部分图片地址是重定向地址 nginx配置 主站地址:https://192.168.123.100/ 主站nginx配置 server {listen 443 ssl;server_name localhost;#ssl证书ssl_certificate ../ssl/ca.crt; #私钥文件ssl_ce…

latin1_swedish_ci(latin1 不支持存储中文、日文、韩文等多字节字符)

文章目录 1、SHOW TABLE STATUS WHERE Name batch_version;2、latin1_swedish_ci使用场景注意事项修改字符集和排序规则修改表的字符集和排序规则修改列的字符集和排序规则修改数据库的默认字符集和排序规则 3、ALTER TABLE batch_version CONVERT TO CHARACTER SET utf8mb4 C…

复健第二天之[MoeCTF 2022]baby_file

打开题目在线环境可以看到: 感觉要用伪协议去求,但是我们并不知道flag的位置,这里我选择用dirsearch去扫一下: 最像的应该就是flag.php了 于是就构建payload: **?filephp://filter/convert.base64-encode/resource…

机器学习之SVD奇异值分解实现图片降维

SVD奇异值分解实现图片降维 目录 SVD奇异值分解实现图片降维1 SVD奇异值分解1.1 概念1.2 基本步骤1.2.1 矩阵分解1.2.2 选择奇异值1.2.3 重建矩阵1.2.4 降维结果 1.3 优缺点1.3.1 优点1.3.2 缺点 2 函数2.1 函数导入2.2 函数参数2.3 返回值2.4 通过 k 个奇异值降维 3 实际测试3…

PyTorch使用教程(6)一文讲清楚torch.nn和torch.nn.functional的区别

torch.nn 和 torch.nn.functional 在 PyTorch 中都是用于构建神经网络的重要组件,但它们在设计理念、使用方式和功能上存在一些显著的区别。以下是关于这两个模块的详细区别: 1. 继承方式与结构 torch.nn torch.nn 中的模块大多数是通过继承 torch.nn…

移动端布局 ---- 学习分享

响应式布局实现方法 主流的实现方案有两种: 通过rem \ vw/vh \ 等单位,实现在不同设备上显示相同比例进而实现适配. 响应式布局,通过媒体查询media 实现一套HTML配合多套CSS实现适配. 在学习移动端适配之前,还需要学习移动端适配原理: 移动端适配原理(Viewport) 了解VSCo…

cuda + cudnn安装

1.安装CUDA Toolkit 在设备管理器(此电脑–右键–属性)的显示适配器中可以查看自己的显卡型号,去下载对应的CUDA Toolkit 。或者输入以下命令查看Driver Version ,cuda Version:12.2代表12.2版本以下兼容可以进行安装 …

DevUI 2024 年度运营报告:开源生态的成长足迹与未来蓝图

在当今数字化飞速发展的时代,开源已成为推动技术创新与协作的重要力量。DevUI 作为开源领域的重要一员,其发展历程与成果备受关注。值此之际,GitCode 精心整理了 DevUI 年度运营报告,为您全面呈现 DevUI 社区在过去一年里的开源之…

python中的RPA->playwright自动化录制脚本实战案例笔记

playwright录制功能使用绕过登录操作 1、首先安装playwright pip install playwright2、 安装支持的浏览器 playwright install # 安装支持的浏览器:cr, chromium, ff, firefox, wk 和 webkit3、接着在自己的项目下运行录制命令: playwright codegen…

如何选择适合特定项目需求的人工智能学习框架?

人工智能学习框架(AI Learning Framework)是一种用于开发、训练和部署人工智能模型的软件平台,旨在简化AI模型的设计、训练和部署过程。这些框架通常提供一系列工具、库和预构建模块,使开发者能够快速实现机器学习任务&#xff0c…

Spring Bug解决

报错: Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type com.itxl.spring6.iocxml.User available: expected single matching bean but found 2: user,user1 at org.sp…

U盘被格式化后的数据救赎与防范策略

一、U盘格式化后的数据困境 在日常的工作与生活中,U盘作为数据传输与存储的重要工具,扮演着不可或缺的角色。然而,当U盘不幸遭遇格式化操作后,存储在其中的宝贵数据瞬间化为乌有,给用户带来极大的困扰。格式化后的U盘…

【AI论文】LlamaV-o1:重新思考大型语言模型(LLMs)中的逐步视觉推理方法

摘要:推理是解决复杂多步骤问题的基本能力,特别是在需要逐步顺序理解的视觉环境中尤为重要。现有的方法缺乏一个全面的视觉推理评估框架,并且不强调逐步解决问题。为此,我们通过三项关键贡献,提出了一个在大型语言模型…

【HTTP】详解

目录 HTTP 基本概念啥是HTTP,有什么用?一次HTTP请求的过程当你在浏览器中输入一个浏览器地址,它会发送什么 ?---(底层流程)HTTP的协议头请求头(对应客户端)一些请求头请求方法 响应头…

EasyExcel - 行合并策略(二级列表)

😼前言:博主在工作中又遇到了新的excel导出挑战:需要导出多条文章及其下联合作者的信息,简单的来说是一个二级列表的数据结构。 🕵️‍♂️思路:excel导出实际上是一行一行的记录,再根据条件对其…

第9章:基于Vision Transformer(ViT)网络实现的迁移学习图像分类任务:早期秧苗图像识别

目录 1. ViT 模型 2. 早期秧苗分类 2.1 数据集 2.2 训练 2.3 训练结果 2.4 可视化网页推理 3. 下载 1. ViT 模型 视觉变换器(ViT)是一种神经网络架构,它将变换器架构的原理应用于视觉数据。最初,Transformers主要用于自然…

ros2-7.5 做一个自动巡检机器人

7.5.1 需求及设计 又到了小鱼老师带着做最佳实践项目了。需求:做一个在各个房间不断巡逻并记录图像的机器人。 到达目标点后首先通过语音播放到达目标点信息, 再通过摄像头拍摄一张图片保存到本地。 7.5.2 编写巡检控制节点 在chapt7_ws/src下新建功…

【React】新建React项目

目录 create-react-app基础运用React核心依赖React 核心思想:数据驱动React 采用 MVC体系package.jsonindex.html好书推荐 官方提供了快速构建React 项目的脚手架: create-react-app ,目前使用它安装默认是19版本,我们这里降为18…

Linux手写FrameBuffer任意引脚驱动spi屏幕

一、硬件设备 开发板:香橙派 5Plus,cpu:RK3588,带有 40pin 外接引脚。 屏幕:SPI 协议 0.96 寸 OLED。 二、需求 主要是想给板子增加一个可视化的监视器,并且主页面可调。 平时跑个模型或者服务,…

网络安全构成要素

一、防火墙 组织机构内部的网络与互联网相连时,为了避免域内受到非法访问的威胁,往往会设置防火墙。 使用NAT(NAPT)的情况下,由于限定了可以从外部访问的地址,因此也能起到防火墙的作用。 二、IDS入侵检…