当前位置:网站首页>多线程进阶(六)----锁机制
多线程进阶(六)----锁机制
2022-04-22 22:07:00 【小曲同学呀】
多线程基础概念:多线程入门(一)
多线程基础实现:多线程入门(二)
多线程管理:多线程基础(三)
线程间的状态转换:多线程基础(四)
线程间的通信:多线程进阶(五)
这篇文章,我们先聊聊java多线程里的"锁"。
首先明确一点:Java多线程的锁都是基于对象的,Java中的每⼀个对象都可以作为⼀个锁
多线程进阶(六)锁机制
1、synchronized
synchronized中文意思是同步,也称之为”同步锁“。作用是保证在同一时刻, 被修饰的代码块或方法只会有一个线程执行,以达到保证并发安全的效果。
1.1 作用域
我们通常使⽤ synchronized 关键字来给⼀段代码或⼀个⽅法上锁。它通常有以下三种形式:
// 关键字在实例⽅法上,锁为当前实例
public synchronized void instanceLock() {
// code
}
// 关键字在静态⽅法上,锁为当前Class对象
public static synchronized void classLock() {
// code
}
// 关键字在代码块上,锁为括号⾥⾯的对象
public void blockLock() {
Object o = new Object();
synchronized (o) {
// code
}
}
1.2 几种锁
一个对象其实有四种锁状态,它们有低到高,依次是:
- 无锁状态
- 偏向锁状态
- 轻量级锁状态
- 重量级锁状态
几种锁,会伴随着竞争情况逐渐升级,锁的升级很容易发生,但是降级发生的条件却十分的苛刻。
下面分别介绍几种锁以及它们之间的锁升级:
1.3 偏向锁
⼤多数情况下锁不仅不存在多线程竞争,⽽且总是由同⼀线程多次获得,于是引⼊了偏向锁。
偏向锁会偏向第一个访问锁的线程,如果在接下来运行过程中,没有别的线程再去竞争该锁,那么持有该锁的线程将永远不会触发同步,提高了程序的运行性能。
1.3.1 原理
原理:当线程第一次进入同步代码块时,会在对象头和栈帧中的锁记录⾥存储锁的偏向的线程ID。当下一次该线程再次进入该同步代码块时,会去对象头的mark word里查看标记,是否存在自己线程的线程ID。
如果是,则表明该线程已经获得了锁,进入同步代码块,不再需要花费CAS操作来加锁解锁。
如果不是,代表是另一个线程来竞争偏向锁,这时候CAS会尝试替换mark word的线程ID为新线程的线程ID。
- 成功,表示老线程ID不存在了,替换成功,这时候还是偏向锁。
- 失败,表示老线程ID依然存在,这时候会升级为轻量级锁,会按照轻量级锁的方式去竞争锁。
1.3.2 升级过程
偏向锁升级轻量级锁时,会暂停使用该锁的线程,重置偏向锁标识,大概过程如下:
- 系统会先找一个没有字节码在执行的时间点,,然后停止拥有该锁的线程;
- 遍历线程栈,修复或修改
mark word,使其变为无锁的状态; - 唤醒被停止的线程,使其变成轻量级锁;
所以,如果应⽤程序⾥所有的锁通常出于竞争状态,那么偏向锁就会是⼀种累赘, 我们可以对其默认关闭。
-XX:UseBiasedLocking=false。
1.4 轻量级锁
多个线程在不同的时段获取同一把锁,不存在锁竞争的情况。
1.4.1 原理
如果一个线程在获得锁的时候发现是轻量级锁,就会把锁的mark word复制到dispace mark word里面。然后尝试使用CAS将锁的mark word替换为指向锁记录的指针。
- 成功,当前线程获得锁;
- 失败,表名
mark word已经被替换成了其他线程的锁记录,接下来会尝试自旋来获取锁,如果一直获取不到,则升级为重量级锁。
一张图说明加锁释放锁的过程:

1.5 重量级锁
重量级锁依赖于操作系统的互斥量(mutex) 实现的,⽽操作系统中线程间状态的转换需要相对⽐较⻓的时间,所以重量级锁效率很低。
当多个线程同时请求某个对象锁时,对象锁会设置⼏种状态⽤来区分请求的线程:
- Contention List:所有请求锁的线程将被⾸先放置到该竞争队列
- Entry List:Contention List中那些有资格成为候选⼈的线程被移到Entry List
- Wait Set:那些调⽤wait⽅法被阻塞的线程被放置到Wait Set
- OnDeck:任何时刻最多只能有⼀个线程正在竞争锁,该线程称为OnDeck
- Owner:获得锁的线程称为Owner
- !Owner:释放锁的线程
当线程释放锁时,会从Contention List或EntryList中挑选⼀个线程唤醒,被选中的线程叫做 Heir presumptive 即假定继承⼈,假定继承⼈被唤醒后会尝试获得锁,但 synchronized 是⾮公平的,所以假定继承⼈不⼀定能获得锁。 这是因为对于重量级锁,线程先⾃旋尝试获得锁,这样做的⽬的是为了减少执⾏操作系统同步操作带来的开销。如果⾃旋不成功再进⼊等待队列。这对那些已经在等待队列中的线程来说,稍微显得不公平,还有⼀个不公平的地⽅是⾃旋线程可能会抢占了Ready线程的锁。
1.6 各种锁对比

1.7 总结锁的升级流程
- 每⼀个线程在准备获取共享资源时: 第⼀步,检查MarkWord⾥⾯是不是放的⾃⼰ 的ThreadId ,如果是,表示当前线程是处于 “偏向锁” 。
- 第⼆步,如果MarkWord不是⾃⼰的ThreadId,锁升级,这时候,⽤CAS来执⾏切换,新的线程根据MarkWord⾥⾯现有的ThreadId,通知之前线程暂停,之前线程 将Markword的内容置为空。
- 第三步,两个线程都把锁对象的HashCode复制到⾃⼰新建的⽤于存储锁的记录空 间,接着开始通过CAS操作, 把锁对象的MarKword的内容修改为⾃⼰新建的记录 空间的地址的⽅式竞争MarkWord。
- 第四步,第三步中成功执⾏CAS的获得资源,失败的则进⼊⾃旋 。
- 第五步,⾃旋的线程在⾃旋过程中,成功获得资源(即之前获的资源的线程执⾏完 成并释放了共享资源),则整个状态依然处于轻量级锁的状态,如果⾃旋失败 。
- 第六步,进⼊重量级锁的状态,这个时候,⾃旋的线程进⾏阻塞,等待之前线程执 ⾏完成并唤醒⾃⼰。
版权声明
本文为[小曲同学呀]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_44427181/article/details/124317820
边栏推荐
- Basic practice of C language (001-1)
- GORM 预加载和自引用
- Reinforcement learning (practice): feedback, AC
- ES6 transforms two-dimensional and multi-dimensional arrays into one-dimensional arrays
- CAS统一身份认证(三):外部独立配置
- GBase 8s V8.8 SQL 指南:教程-6.2.1(4)
- hawe哈威液压泵站的液压冲击分析
- [MRCTF2020]Ez_bypass
- opcua协议如何在appinventor上使用?
- O0 O1 O2 O3优化原理
猜你喜欢

Best buy website EDI test process

Flex layout
![[Istio是什么?] 还不知道你就out了,一文40分钟快速理解](/img/c4/07d319932bddf2eede47bd2078f777.png)
[Istio是什么?] 还不知道你就out了,一文40分钟快速理解

Centos7安装mysql
![[4.1] trigger trigger and evictor cleaner of flick window operator](/img/68/9a32ba6cc484237cd2c7015e5179be.png)
[4.1] trigger trigger and evictor cleaner of flick window operator
![[Luogu] p1162 filling color (BFS)](/img/08/8db3f1b36d222eb489a456de814d56.png)
[Luogu] p1162 filling color (BFS)

Youqilin 22.04 lts version is officially released | ukui 3.1 opens a new experience!

不能再简单的意向锁

未来可期,PlatoFarm的生态通证登录Bitmart等全球四大平台

【洛谷】P1162 填涂颜色(bfs)
随机推荐
外部中断---------stm32f407zet6
2.56-试着用不同的示例值来运行show_bytes的代码。
GBase 8s V8.8 SQL 指南:教程-6.2.2(1)
【 luogu】 p1162 couleur de remplissage (bfs)
2.60-假设我们将一个w位的字中的字节从0(最低位)到w/8- 1(最高位)编号。写出下面C函数的代码,它会返回一个无符号值,其中参数x的字节i被替换成字节b。
[intranet penetration] - vulnstack (I)
Svg series - 3, powerful path
No more simple intention lock
跨域问题及Umi-proxy代理解决跨域问题
GBase 8s V8.8 SQL 指南:教程-6.2.1(2)
51 MCU proteus simulation key control nixie tube digital display
2.57-编写程序show_short, show_long和show_double,它们分别打印类型为short, long和double的C语言对象的字节表示。请试着在几种机器上运行。
snap安装repo问题
餐饮行业收银系统源码,C# .NET + MSSQL WPF
SMB+MSSQL
[mmub] mobile phone user behavior modeling based on Hidden Markov Model -- hidden Markov model
They are all intelligent in the whole house. What's the difference between aqara and homekit?
What is the magic of moonbirds NFT, which became popular overnight?
企业应如何制定云计算使用中的灾难恢复计划?
Reinforcement learning (practice): dqn, double dqn, dueling dqn