网站首页 > 文章精选 正文
目录
1、乐观锁和悲观锁的概念
2、synchronized底层的原理
3. CAS的原理
4. 并发包下Lock锁和synchronized对比
5. 锁升级原理
大家好,我是四九城最豪横的小耳朵。
今天咱们来用大白话聊聊synchronized、CAS底层原理、Lock锁和锁升级原理。
1、乐观锁和悲观锁的概念
比如线程A对某个变量进行修改,在这个修改期间,它持悲观心理,认为其他线程在这个期间,也有可能去修改这个变量,所以它就给变量加个锁,保证在它修改期间,别的线程没法去访问这个变量。这个锁就是悲观锁。悲观锁是重量级锁,代表对象synchronized关键字。
比如线程A对某个变量进行修改,在这个修改期间,它持乐观心理,认为其他线程在这个期间,不会去修改这个变量,所以它只在执行修改操作的时候,才会给变量加个锁。这个锁就是乐观锁。乐观锁是轻量级锁,代表对象CAS。
2、synchronized底层的原理
首先需要知道一个概念——monitor。
每个对象内部都有一个monitor,monitor里面有一个计数器,从0开始的。
如果这个线程想获取monitor的锁,就先判断monitor的计数器是不是为0,如果为0,说明没人获取锁,这个线程就可以获取锁,然后对计数器加1;如果不为0,说明已经有其他线程已经获取了锁,这个线程就必须阻塞等待。
synchronized底层的原理是跟jvm指令和monitor有关系的。
synchronized一般是对对象加锁,对类加锁也就是对类对象加锁。
如果使用了synchronized关键字,在底层编译后的jvm指令中,会有monitorenter和monitorexit两个指令。线程进入synchronized代码片段,执行monitorenter指令对monitor计数器加1,这样其他线程发现monitor的计数器不为0,就阻塞等待;
线程出synchronized代码片段,执行monitorexit指令就是对monitor计数器减1,这样其他线程发现monitor的计数器为0,就可以拿到锁,给monitor的计数器加1,然后执行业务逻辑了。
上面的是针对synchronized对对象、类加锁的底层原理。方法加锁不是通过monitor指令,而是通过ACC_SYNCHORNIZED关键字,判断方法是否同步。
3. CAS的原理
CAS,Conmpare And Swap,英文翻译过来就是“比较和交换”。它的过程是3步,第一步是读值,第二步比较值,看值和自己刚刚读的一不一样,第3步是修改,如果值跟自己读的一样,就修改。
CAS最经典的实现类就是AtomicInteger。
比如2个线程同时要对AtomicInteger加1,A线程读旧值,是0,B线程也读旧值,是0,A这时执行CAS操作,比较值,发现值和刚刚自己读的一样,都是0,然后它修改值为1;B线程执行CAS操作,比较值,发现值和刚刚自己读的不一样,变成1了,它相当于又读了一遍旧值,将自己内存中的旧值改为1,然后继续执行CAS操作。
CAS在底层的硬件级别给你保证一定是原子的,同一时间只有一个线程可以执行CAS,先比较再设置,其他的线程的CAS同时间去执行此时会失败。
CAS的bug是会出现的问题 ABA 空循环。如果要解决ABA 问题,可以使用AtomicStampedReference类, 它内部用类似创建版本号的方式来解决 ABA 问题。
4. 并发包下Lock锁和Synchronized对比
我觉得二者的主要区别是以下四点;
1.首先synchronized是java内置关键字,是jvm层面,Lock是个java类,是jdk层面;
2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
3.synchronized会自动释放锁,Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
4.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
5. 锁升级原理
一开始是无锁的状态,一上来会先去判断一下有没有锁,有锁的话最开始的时候锁是支持偏向锁的。偏向锁当前获取到锁资源的这个线程,我会优先让他再去获取这个锁,如果它没获取到这个锁,就升级为一个轻量级的,一个cas锁,即乐观锁,乐观锁的时候它是一个比较和交换的过程,如果没有设置成功的话,它会进行一个自旋,然后自旋到一定次数之后才会升级成一个synchronized的这样一个重量级的锁,这样的话他就保证了性能的问题。你想想如果一开始就是synchronized这样一个重量级的锁,那性能就比较差了。
End
作者简介:豪横的小耳朵,一个豪横的程序员。想和大家一起在技术的世界里豪横,用技术的眼光去看待世界。欢迎搜索公众号“豪横的小耳朵”,持续关注,一大波原创系列文章正在路上
扫码关注后回复“666”,可免费获取下图所示的java高级工程师学习资料一份。
猜你喜欢
- 2025-07-10 Java基础——Java多线程(Lock接口详解)
- 2025-07-10 高并发环境下诡异的加锁问题(你加的锁未必安全)
- 2025-07-10 你真正了解synchronized关键字吗?
- 2025-07-10 Java 并发之 ReentrantReadWriteLock 深入分析
- 2025-07-10 ReentrantLock源码解析:ReentrantLock 的实现原理与 AQS 机制
- 2025-07-10 「Java多线程」内置锁(Synchronized)的前世今生
- 2025-07-10 面试:如何保证接口的幂等性?常见的实现方案有哪些?
- 2025-07-10 聊聊并发编程: Lock(并发编程的三大特性)
- 2025-07-10 Java并发之旅:Lock, Condition & ReadWriteLock 的魔法
- 2025-07-10 对volatile,synchronized,AQS的加锁解锁原理的一些理解
- 最近发表
-
- Vue3+Django4全新技术实战全栈项目|高清完结
- 工厂模式+策略模式消除 if else 实战
- 每天一个 Python 库:httpx异步请求,让接口测试飞起来
- 如何高效实现API接口的自动化测试?
- 前端工程化:从“手忙脚乱”到“从容协作”的进化记
- 使用C#创建服务端Web API(c#开发web服务器)
- SpringBoot之旅第四篇-web开发(springboot做web项目)
- 一文读懂SpringMVC(一文读懂新型政策性金融工具)
- Rust Web编程:第十二章 在 Rocket 中重新创建我们的应用程序
- Apache Druid 数据摄取——本地数据和kafka流式数据 一篇文章看懂
- 标签列表
-
- newcoder (56)
- 字符串的长度是指 (45)
- drawcontours()参数说明 (60)
- unsignedshortint (59)
- postman并发请求 (47)
- python列表删除 (50)
- 左程云什么水平 (56)
- 计算机网络的拓扑结构是指() (45)
- 编程题 (64)
- postgresql默认端口 (66)
- 数据库的概念模型独立于 (48)
- 产生系统死锁的原因可能是由于 (51)
- 数据库中只存放视图的 (62)
- 在vi中退出不保存的命令是 (53)
- 哪个命令可以将普通用户转换成超级用户 (49)
- noscript标签的作用 (48)
- 联合利华网申 (49)
- swagger和postman (46)
- 结构化程序设计主要强调 (53)
- 172.1 (57)
- apipostwebsocket (47)
- 唯品会后台 (61)
- 简历助手 (56)
- offshow (61)
- mysql数据库面试题 (57)