JavaEE-常见的锁策略和synchronized的锁机制
目录
- 常见的锁策略
- 乐观锁和悲观锁
- 轻量级锁和重量级锁
- 自旋锁和挂起等待锁
- 普通互斥锁和读写锁
- 公平锁和非公平锁
- 可重入锁和不可重入锁
- synchronized的锁机制
- synchronized特性
- 锁升级/锁膨胀
- 锁消除
- 锁粗化
常见的锁策略
乐观锁和悲观锁
乐观锁和悲观锁主要是看主要是锁竞争的激烈程度.
- 乐观锁预测锁竞争不是很激烈, 做的准备工作相对更少, 开销更小, 效率更高.
- 悲观锁预测锁竞争会很激烈, 做的准备工作相对更多, 开销更大, 效率更低.
乐观锁一般应用在线程的冲突比较少, 也就是说读操作比较多, 写操作比较少; 而悲观锁则相反, 一般应用在线程冲突比较多, 写操作比较多的情况下.
轻量级锁和重量级锁
轻量级锁和重量级锁看的是锁操作的开销大不大.
- 重量级锁: 加锁解锁的开销比较的大, 需要在内核态中进行完成, 多数情况下悲观锁是一个重量级锁, 但并不绝对.
- 轻量级锁: 它的开销比较小, 在用户态中完成就行, 多数情况下乐观锁是一个轻量级锁, 但并不绝对.
轻量级锁一般都是通过版本号机制或CAS算法进行实现的, 但对用重量级锁来说不是, 这与操作系统的内核有关, cpu一般会提供一些特殊指令, 操作系统会对这些指令进行封装一层, 提供一个mutex(互斥量), 在Linux种就会提供一个这样的接口供用户进行加锁解锁; 一般来说, 如果锁是通过调用mutex来进行实现的, 那么这个锁就是一个重量级锁. 自旋锁和挂起等待锁
- 自旋锁: 是一种典型的轻量级锁, 在线程获取不到锁的情况下, 不会立刻放弃CPU, 而是会一直快速频繁进行获取, 直到获取到锁; 这种策略的优势在于节省线程调度的开销并且更能及时的获取到锁, 缺点就是更加浪费cpu资源.
- 挂起等待锁: 是一种典型的重量级锁, 线程在获取不到锁的情况下会堵塞等待(放弃CPU,进入等待队列), 然后等到锁被释放的时候再有操作系统调度.
普通互斥锁和读写锁
Java中synchronized的锁就是一个普通的互斥锁, 只涉及两个操作:
- 加锁
- 解锁
多线程针对同一个变量并发读, 这个时候没有线程安全问题的, 也不需要加锁控制, 对于读写锁来说, 分成了三个操作:
加读锁:如果代码只进行了读操作,就加读锁 加写锁:如果代码进行了修改操作,就加写锁 解锁: 针对读锁和读锁之间,是不存在互斥关系的读锁和读锁之间是没有互斥的, 读锁和写锁之间, 写锁和写锁之间, 才需要互斥
在java中有读写锁的标准类, 位于java.util.concurrent.locks.ReentrantReadWriteLock, 其中ReentrantReadWriteLock.ReadLock为读锁,ReentrantReadWriteLock.WriteLock为写锁.公平锁和非公平锁
- 公平锁: 多个线程在等待一把锁的时候, 遵循先来后到原则, 谁是先来的, 谁就先获得这把锁.
- 非公平锁: 多个线程等待同一把锁, 不遵守先来后到原则, 每个人等待线程获取锁的概率是均等的.
操作系统中原生的锁都是 “非公平锁”, 操作系统中的针对加锁的控制, 是依赖于线程调度顺序的, 这个调度顺序是随机的, 不会考虑到这个线程等待锁多久了, 如果想要实现公平锁, 就得在这个基础上加额外的数据结构(比如引入一个队列), 来记录线程的先后顺序.
可重入锁和不可重入锁
- 可重入锁: 一个线程针对同一把锁连续加锁两次,不会出现死锁
- 不可重入锁: 一个线程针对同一把锁连续加锁两次,会出现死锁
synchronized的锁机制
synchronized特性
Synchronized具有如下特性:既是乐观锁也是悲观锁, 当锁竞争较小时它就是乐观锁(默认), 锁竞争较大时它就是悲观锁. 是普通互斥锁。 既是轻量级锁(默认)也是重量级锁, 根据锁竞争激烈程度自适应. 轻量级锁部分基于自旋锁实现, 重量级锁部分基于挂起等待锁实现. 是非公平锁. 是可重入锁.锁升级/锁膨胀
JVM 将 synchronized 锁分为 无锁, 偏向锁, 轻量级锁, 重量级锁 状态; 会根据情况, 进行依次升级.

-
当没有线程加锁的时候, 此时为无锁状态.
-
当首个线程进行加锁的时候, 此时进入偏向锁的状态, 偏向锁不是真的加锁, 而是在对象头做个标记(这个过程是非常轻量的).
偏向锁并不是真的加锁, 只是做了一个标记, 这样带来的好处就是, 后续如果没人竞争的时候, 就一直不去确立关系(节省了确立关系/分手的开销), 如果没有其他的线程来竞争这个锁, 就不必真的加锁(节省了加锁解锁的开销), 如果在执行代码过程中, 并没有其他线程来尝试加锁, 那么在执行完synchronized之后, 取消偏向锁即可.
-
当有其他线程进行加锁, 导致产生了锁竞争时, 此时进入轻量级锁状态(迅速的把偏向锁升级成真正的加锁状态).
此处的轻量级锁是通过 CAS 来实现的, 如果其他的线程很快的释放锁, 那么我们的自旋锁是非常合适的, 但是如果其他线程长时间占用锁, 自旋锁就不太合适了, 自旋操作是一直让 CPU 空转, 比较浪费 CPU 资源, 因此此处的自旋不会一直持续进行, 而是达到一定的时间/重试次数, 就不再自旋了, 也就是所谓的 “自适应”.
-
如果竞争进一步加剧, 进入重量级锁状态(挂起等待锁).
重量级锁是基于操作系统原生的一组API(mutex)来进行加锁了, 当我们自旋不能快速获取到锁时, 锁竞争加剧, 就会升级为重量级锁, 我们的线程就会被放到阻塞队列中, 暂时不参与CPU调度, 当锁被释放了之后, 线程才有机会被调度, 从而有机会获取到锁, 一旦线程被切换出cpu, 这就是比较低效的事情了.
锁消除
锁消除是编译器的智能判定, 有些代码, 编译器认为没有加锁的必要, 就会自动把你加的锁自动去除, 比如字符串相关的线程安全类
StringBuffer, 这个类中的关键方法都带有synchronized, 但是当我们在单线程环境下使用StringBuffer,不会涉及到线程安全问题, 此时编译器就会直接把这些加锁操作去除了.锁粗化
锁粗化就是将
synchronized的加锁代码块范围增大, 加锁的代码块中的代码越多, 锁的粒度就越粗, 否则锁的粒度就越细.通常情况下, 认为锁的粒度细一点比较好, 加锁的部分的代码, 是不能并发执行的, 锁的粒度越细, 能并发的代码就越多, 反之就越少.
但是有些情况下, 锁的粒度粗一些反而更好, 如果我们两次加锁解锁的时间间隙非常小, 分开加锁会造成额外的资源开销, 而且中间间隙很小, 就算并发效果也不是很明显, 这种情况下不如直接一把大锁搞定.
-
相关文章:
JavaEE-常见的锁策略和synchronized的锁机制
目录常见的锁策略乐观锁和悲观锁轻量级锁和重量级锁自旋锁和挂起等待锁普通互斥锁和读写锁公平锁和非公平锁可重入锁和不可重入锁synchronized的锁机制synchronized特性锁升级/锁膨胀锁消除锁粗化常见的锁策略 乐观锁和悲观锁 乐观锁和悲观锁主要是看主要是锁竞争的激烈程度.…...
信息化,数字化,智能化是三种不同的概念吗?
前两年流行“信息化”,网上铺天盖地都是关于“信息化”的文章,这两年开始流行起“数字化”,于是铺天盖地都是“数字化”的文章。(这一点从数字化和信息化这两个关键词热度趋势就可以看出来)。 但点开那些文章仔细看看…...
【华为OD机试 2023最新 】 匿名信(C++ 100%)
题目描述 电视剧《分界线》里面有一个片段,男主为了向警察透露案件细节,且不暴露自己,于是将报刊上的字减下来,剪拼成匿名信。 现在又一名举报人,希望借鉴这种手段,使用英文报刊完成举报操作。 但为了增加文章的混淆度,只需满足每个单词中字母数量一致即可,不关注每个…...
硬件语言Verilog HDL牛客刷题day05 时序逻辑部分
1.VL29 信号发生器 1.题目: 题目描述: 请编写一个信号发生器模块,根据波形选择信号wave_choise发出相应的波形:wave_choice0时,发出方波信号;wave_choice1时,发出锯齿波信号;wave…...
Ajax 入门
前端技术:在浏览器中执行的程序都是前端技术。如 html、css、js 等 后端技术:在服务器中执行的长须,使用 Java 等语言开发的后端程序。servlet,jsp,jdbc,mysql,tomacat 等 全局刷新 使用表单…...
半导体器件基础06:发光二极管
说在开头:关于玻尔原子模型(1) 卢瑟福的模型面临着与经典电磁波理论的矛盾,按照经典电磁波理论,卢瑟福的原子不可能稳定存在超过1秒钟。玻尔面临着选择:要么放弃卢瑟福模型,要么放弃麦克斯韦伟…...
AutoCV第二课:Python基础
目录Python基础前言1.流程控制1.1 条件语句1.2 循环语句1.2.1 while循环语句1.2.2 for循环语句1.3 作业1.4 拓展-try except语法2.函数2.1 函数定义2.2 函数的参数2.2.1 位置参数2.2.2 命名参数2.2.3 默认参数2.2.4 可变参数2.2.5 参数展开2.3 递归函数2.3.1 递归函数定义2.3.2…...
LeetCode算法 打家劫舍 和 打家劫舍II C++
目录题目 打家劫舍参考答案题目 打家劫舍II参考答案题目 打家劫舍 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯…...
蓝桥杯刷题冲刺 | 倒计时10天
作者:指针不指南吗 专栏:蓝桥杯倒计时冲刺 🐾马上就要蓝桥杯了,最后的这几天尤为重要,不可懈怠哦🐾 文章目录1.有边数限制的最短路2.九进制转十进制1.有边数限制的最短路 题目 链接: 853. 有边数…...
个人练习-Leetcode-剑指 Offer II 109. 开密码锁
题目链接:https://leetcode.cn/problems/zlDJc7/ 题目大意:给出一个四位数字的密码锁,初始状态是0000,目标是targer。每一次转动只能让一个位的轮盘转动一下(0往后转是9)。有一个vector<string> dea…...
四个常见的Linux面试问题
四个常见的Linux面试问题。 刚毕业要找工作了,只要是你找工作就会有面试这个环节,那么在面试环节中,有哪些注意事项值得我的关注呢?特别是专业技术岗位,这样的岗位询问一般都是在职的工程师,如何在面试环节…...
15、接口(C#)
15.1 什么是接口 接口是指定一组函数成员而不实现它们的引用类型。所以只能类和结构实现接口。 15.2 声明接口 接口声明不能包含以下成员 数据成员静态成员 接口声明只能包含以下类型的费静态成员函数声明: 方法事件索引器属性 这些函数成员的声明不能包含任何实…...
C++中常见的容器类使用方法举例(vector、deque、map、set)
cpp中常见的容器类有vector、list、deque、map、set、unordered_map和unordered_set。 下面将举例直接说明各个容器的使用方法。 文章目录综合示例1. vector:动态数组,支持随机访问2. list:双向链表,支持双向遍历和插入删除3. de…...
什么是强缓存和协商缓存
什么是缓存 浏览器缓存就是把一个已经请求过的Web资源(如html页面,图片,js,数据等)拷贝一份副本储存在浏览器中。缓存会根据进来的请求保存输出内容的副本。当下一个请求来到的时候,如果是相同的URL&#…...
算法刷题之堆
1. heapq 堆 Python 中只有最小堆: import heapqa [] heapq.heappush(a, 3) # 添加元素 heapq.heappush(a, 2) heapq.heappush(a, 1) while len(a): # 判断堆的长度print(heapq.heappop(a)) # 弹出堆顶元素# 将列表转换为最小堆 nums [2, 3, 1, 4, 5, 6] hea…...
javaweb导师选择系统
本文以JSP为开发技术,实现了一个导师选择系统。导师选择系统分为三大模块,包括管理员:学员信息管理、导师信息管理、导师选择管理、导师分布图、公告信息管理、系统管理,学生:个人资料管理、导师选择管理、导师分布图管…...
LeetCode150 逆波兰表达式求值
题目: 给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。请你计算该表达式。返回一个表示表达式值的整数。 注意: 有效的算符为 ‘’、‘-’、‘*’ 和 ‘/’ 。每个操作数(运算对象)都可以…...
【Node.js】项目开发实战(中)
开发用户的注册和登录接口步骤1,打开MySQL Workbench,打开自己的数据库进入创建用户信息表新建 ev_users表安装并配置mysql模块安装mysql模块新建db文件夹下index.js,导入并配置mysql模块安装bcryptjs对密码进行加密处理新建/router_handler/user.js中&a…...
记录一次 New Bing 英语陪练
记录一次 New Bing 英语陪练 Now I start to speak english to chat with you . Help me find the mistake in my word and help me improve my english I’m glad you want to practice your English with me. I can help you find the mistakes in your words and help you i…...
【Python】照片居然能变素描?不会画画但是咱会代码
文章目录前言一、准备二、下载预训练模型总结前言 Photo-Sketching 一个能将照片的轮廓识别出来并将其转化为“速写”型图像的开源模块。 比如,这只小狗: 经过模型的转化,会变成卡通版的小狗: 非常秀,这很人工智能…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果:def __in…...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
