Java 中的各种锁

​ Java 中我们经常听到各种锁,例如悲观锁,乐观锁,自旋锁等等。今天我们将 Java 中的所有锁放到一起比较一下,并分析各自锁的特点,让大家能够快捷的理解相关知识。

1、悲观锁 VS 乐观锁

从概念上来说

悲观锁:

​ 在对同一个数据并发操作的时候,认为使用到的数据一定被别的线程修改数据,因此获取数据的时候先加锁,保证数据不会被别的线程修改。例如: 常用的 synchronized 关键字和 Lock 的实现类的使用都是悲观锁

乐观锁:

​ 同悲观锁相反,认为使用到的数据不会有别的线程修改数据,所以不会添加锁,只有在更新数据的时候去判断之前有没有别的线程更新了这个数据。如果没有被更新,则将修改数据写入,如果数据被其他线程修改,则报错或者重试。通常是使用 CAS 算法操作。

从上面的概念来说

悲观锁适合写操作多的场景: 先加锁可以保证写操作时数据正确

乐观锁适合读操作多的场景: 不加锁的特点能够使其读操作的性能大幅提高。

代码实例:

悲观锁的调用方式

//synchronized使用方式
public synchronized void testMethod() {
	// 操作同步业务
}

// ReentrantLock
private ReentrantLock lock = new ReentrantLock();

public void modifyPublicResources() {
	lock.lock();
	// 操作同步业务
	lock.unlock();
}

乐观锁的调用方式

private AtomicInteger atomicInteger = new AtomicInteger(); 
atomicInteger.incrementAndGet(); //执行自增,底层是 CAS 算法。

总结:

其实悲观锁和乐观锁的区别在于是否锁住同步资源. 如果锁住同步资源就是悲观锁,不锁住同步资源就是乐观锁。

2、自旋锁 VS 适应性自旋锁

Java 线程状态切换是需要操作系统操作 CPU 状态完成的。也就是需要从用户态切换到内核态。在很多场景下,同步资源的锁定还是非常短,为了这一段时间去调用内核去切换进行状态,就会得不偿失。因此优化这种情况,我们不如让线程‘等一等’。例如自旋一下,当同步资源立即的时候,能够立即操作,提升速度。

请添加图片描述
请添加图片描述

从上面两张图,我们可以看下自旋锁和非自旋锁的区别。

自旋锁本身也是有缺点的,它不能代替阻塞。短暂自旋等待可以提高同步资源的加锁速度。但是长时间的自旋,就会白白浪费 CPU, 因此一般我们会尝试设置自旋次数,如果超过这个次数之后,就应该挂起线程。

可以通过-XX:PreBlockSpin参数来设置一个大致的自旋次数范围。

在实际应用中,一般不建议手动设置这个参数,因为自适应自旋锁通常能够做出更合理的决策。

讲完自旋锁,那我们看下自适应自旋锁。

所谓的自适应自旋锁就是自旋次数不是固定的。而是由前一次在同一个锁上的自旋时间以及锁的拥有者的状态来决定。如果在同一个锁对象上,自旋等待刚刚成功获取过锁。并且持锁的线程并在运行,那么虚拟机就会认为这次自旋很有可能再次成功,进而允许自旋等待持续相对更长的时间。例如前一次是 10次循环,这次可能是 20 次循环

总结:

其实所谓的自旋锁就是在加锁同步资源失败的时候,线程不阻塞的情况。

3、无锁 VS 偏向锁 VS 轻量级锁 VS 重量级锁

img

以上是 synchronized 锁膨胀的过程,其中涉及到无锁,轻量级锁,重量级锁的转变过程。这里我们先大致了解一下。synchronized 锁膨胀的详细过程 后续详细分析。

总结:

​ 偏向锁通过对比Mark Word解决加锁问题,避免执行CAS操作。而轻量级锁是通过用CAS操作和自旋来解决加锁问题,避免线程阻塞和唤醒而影响性能。重量级锁是将除了拥有锁的线程以外的线程都阻塞

4、公平锁 VS 非公平锁

请添加图片描述

上图是 AQS 的整体原理图。所谓的公平和非公平就是如果获取不到同步资源的时候,进入队列的方式是否公平。

在 java 1.8 中 ReentrantLock的实现方式 分为公平锁和非公平锁两种实现方式。其中主要区别就是在进入队列的时候,是否尝试去加锁。

公平锁: 如果获取不到同步资源就是乖乖的进入阻塞队列

非公平锁: 如果获取不到同步资源,在进入队列之前,再次去尝试加锁。如果加锁失败才会进去到队列中。

非公平锁比较常用

因为非公平锁的效率较高。同步加锁的时候比较短,在进队列之前再重新加锁的时候,有可能获加锁成功。并且线程不会进入到内核态,并阻塞唤醒。

5、可重入锁和非可重入锁

public class Reentrant {

    public synchronized void method1() {
        System.out.println("method1");
        method2();
    }

    public synchronized void method2() {
        System.out.println("method2");
    }
}

我们看下上面的代码。当一个线程调用 method1()的时候,需要加锁成功。当调用 method2()的时候,也需要加锁成功才能执行。如果synchronized 内置锁是不可重入的。那就会导致无法调用 method2()。因为调用 method1()的加锁成功,但是锁未释放。就会导致调用 method2()方法 无法加锁成功。造成死锁问题。

synchronized 和 ReentrantLock 都是比较常见的重入锁。

总结:

​ 如果一个线程在多个流程中能获取到同一把锁,就是可重入锁。如果不能,则是非可重入锁。

6、独享锁和共享锁

独享锁,是指该锁一次只能被一个线程所持有。如果线程A对数据x加上排它锁后,则其他线程不能再对x加任何类型的锁。获得排它锁的线程即能读数据又能修改数据。

共享锁 是指该锁可被多个线程所持有。如果线程A对数据x加上共享锁后,则其他线程只能对x再加共享锁,不能加排它锁。获得共享锁的线程只能读数据,不能修改数据。

总结:

多个线程能不能共享一把锁?如果能,就是共享锁,如果不能 就是排他锁。

以上就是 Java 中的各种锁。部分锁的内容讲的比较概括,后续会有文章详细地讲解。

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

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

相关文章

iOS Masonry对包体积的影响

01 Masonry介绍 Masonry是iOS在控件布局中经常使用的一个轻量级框架,Masonry让NSLayoutConstraint使用起来更为简洁。Masonry简化了NSLayoutConstraint的使用方式,让我们可以以链式的方式为我们的控件指定约束。 常用接口声明与实现: 使用方式…

Flink源码解析之:如何根据StreamGraph生成JobGraph

Flink源码解析之:如何根据StreamGraph生成JobGraph 在上一章节中,我们讲解了Flink如何将用户自定义逻辑算子转换成StreamGraph。在生成StreamGraph的过程中,Flink内部没有做任何优化,只是将用户自定义算子和处理流程转换成了Stre…

Docker Container 可观测性最佳实践

Docker Container 介绍 Docker Container( Docker 容器)是一种轻量级、可移植的、自给自足的软件运行环境,它在 Docker 引擎的宿主机上运行。容器在许多方面类似于虚拟机,但它们更轻量,因为它们不需要模拟整个操作系统…

google广告 google分析

这里写自定义目录标题 google广告AFC类型广告AFS类型广告CSE广告RS广告 google分析监听广告点击click事件(广告追踪) google广告 AFS广告主要是指嵌入在搜索引擎上的广告,用户在进行搜索时看到的广告,与搜索关键词息息相关。 AFC…

【开源免费】基于SpringBoot+Vue.JS网上摄影工作室系统(JAVA毕业设计)

本文项目编号 T 103 ,文末自助获取源码 \color{red}{T103,文末自助获取源码} T103,文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

基于SSM的“电器网上订购系统”的设计与实现(源码+数据库+文档+PPT)

基于SSM的“电器网上订购系统”的设计与实现(源码数据库文档PPT) 开发语言:Java 数据库:MySQL 技术:SSM 工具:IDEA/Ecilpse、Navicat、Maven 系统展示 系统首页 商品类型 商品管理 订单展示 商品购物车 登录页面 …

【物联网】给EoRa Pi 烧录Meshtastic

文章目录 一、Meshtastic 是什么?二、Meshtastic 烧录过程1. 在线烧录工具2. 刷机进度 总结 一、Meshtastic 是什么? Meshtastic 是一种基于 LoRa 技术的离网通信平台。它通过低成本、低功耗的无线电设备,实现远距离自组网通信。可在脱离现有…

宝塔-firefox(Docker应用)-构建自己的Web浏览器

安装基础软件 宝塔中安装firefox(Docker应用) 。宝塔中需要先安装docker及docker-composefirefox配置安装 点击firefox应用,选择【安装配置】点击右边绿色按钮,进行安装,这一步等待docker-compose根据你的配置初始化docker应用 等待安装 …

windows 本地node版本快速升级

文章目录 前言一、前置条件二、使用步骤1.查看node 安装位置2.下载指定的node 版本3.下载后进行解压缩4. 删除覆盖原来的node文件夹内容5. 验证 总结 前言 Node.js 是一个开源、跨平台的JavaScript运行时环境,它允许开发者在服务器端运行JavaScript代码。Node.js 基…

HTML4笔记

尚硅谷 一、前序知识 1.认识两位先驱 2.计算机基础知识 3.C/S架构与B/S架构 4.浏览器相关知识 5.网页相关概念 二、HTML简介 1.什么是HTML? 2.相关国际组织(了解) 3.HTML发展历史(了解)** 三、准备工作 1.常用电脑设置 2.安装Chrome浏览器 四、HTML入门 1.HTML初体验 2.H…

跟着逻辑先生学习FPGA-实战篇第二课 6-2 LED灯流水灯实验

** 硬件平台:征战Pro开发板 软件平台:Vivado2018.3 仿真软件:Modelsim10.6d 文本编译器:Notepad** 征战Pro开发板资料 链接:https://pan.baidu.com/s/1AIcnaGBpNLgFT8GG1yC-cA?pwdx3u8 提取码:x3u8 1 知识背景 我们在《LED 灯…

【已解决】Latex中高亮段内命令(如参考文献引用、图、表格)

速览:解决前后图片对比拟解决的问题问题描述Latex高亮的一般做法段内有命令时候的高亮报错 问题原因 解决方案——在导言区为 \cite 等命令“注册”解决方案简要描述详细解释其他情况 速览:解决前后图片对比 解决前: 解决后: …

CSS中的“display“

简单记录一下,看图理解~(图片来自于MDN Web)

数字图像处理

一 形态学处理 ①二值图像 PS:1(255)代表的是白 0代表的是黑(0就是什么都看不见,就是黑) ②灰度图像 ③彩色图像 ④数学形态学基础:是分析几何形状和结构的数学方法,它建立在…

【项目日记(7)】第三层:页缓存的具体实现(上)

目录 前言1. 页缓存的具体结构2. 页缓存分配内存的全过程3. 页缓存分配内存的代码实现4. 优化代码,并完全脱离malloc5. 总结以及代码拓展 前言 在页缓存这一层中,负责给中心缓存分配大块儿的内存,以及合并前后空闲的内存,这一层为…

Python + 深度学习从 0 到 1(03 / 99)

希望对你有帮助呀!!💜💜 如有更好理解的思路,欢迎大家留言补充 ~ 一起加油叭 💦 欢迎关注、订阅专栏 【深度学习从 0 到 1】谢谢你的支持! ⭐ 神经网络的数据表示 – 张量 你可能对矩阵很熟悉&a…

使用Docker-compose部署SpringCloud项目

docker编写dockfile遇到的问题: 需要在docker-compose.yml文件下执行命令 docker-compose.yml文件格式的问题 1和2处空2格,3处空1格,4为本地配置文件目录,5为docker容器的目录,version为自己安装的docker-compose版本 …

【机器学习】【朴素贝叶斯分类器】从理论到实践:朴素贝叶斯分类器在垃圾短信过滤中的应用

🌟 关于我 🌟 大家好呀!👋 我是一名大三在读学生,目前对人工智能领域充满了浓厚的兴趣,尤其是机器学习、深度学习和自然语言处理这些酷炫的技术!🤖💻 平时我喜欢动手做实…

Tonghttpserver6.0.1.3 使用整理(by lqw)

文章目录 1.声明2.关于单机版控制台和集中管理控制台3.单机版控制台3.1安装,启动和查看授权信息3.2一些常见的使用问题(单机控制台)3.3之前使用的是nginx,现在要配nginx.conf上的配置,在THS上如何配置3.4如何配置密码过…

BUUCTF Pwn ciscn_2019_es_2 WP

1.下载 checksec 用IDA32打开 定位main函数 发现了个假的后门函数: 看看vul函数: 使用read读取 想到栈溢出 但是只有48个 只能覆盖EBP和返回地址 长度不够构造 所以使用栈迁移: 栈迁移需要用到leave ret 使用ROPgadget找地址: …