java架构之路(多线程)synchronized详解以及锁的膨胀升级过程
4: astore_1
5: monitorenter
6: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
9: ldc #4 // String 只有我拿到锁啦
11: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
14: aload_1
15: monitorexit
16: goto 24
19: astore_2
20: aload_1
21: monitorexit
22: aload_2
23: athrow
24: return
Exception table:
from to target type
6 16 19 any
19 22 19 any
明显看到了两个很重要的方法monitorenter和monitorexit两个方法,也就是说我们的
【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】浏览器打开:qq.cn.hn/FTf 免费领取
synchronized方法加锁是基于monitorenter加锁和monitorexit解锁来操作的
我们得知是由monitorenter来控制加锁和monitorexit解锁的,我们完全可以这样来操作。上次我们说过一个unsafe类。
public class SynchronizedTest {
private static Object obj = new Object();
public void lockMethod(){
UnsafeInstance.reflectGetUnsafe().monitorEnter(obj);
}
public void unLockMethod(){
UnsafeInstance.reflectGetUnsafe().monitorExit(obj);
}
}
就是我们上次说的unsafe那个类给我们提供了加锁和解锁的方法,这样就是实现夸方法的加锁和解锁了,但是超级不建议这样的使用,后面的AQS回去说别的方式。越过虚拟机直接操作底层的,我们一般是不建议这样来做的。
我们还可以将synchronized锁放置在方法上。例如
public class SynchronizedTest {
private static Object object = new Object();
public static synchronized void lockMethod() {
System.out.println(“只有我拿到锁啦”);
}
}
这样加锁是加在了this当前类对象上的。如果不加static,锁是加在类对象上的,需要注意我们用的spring的bean作用域
并且我们的synchronized是一个可重入锁,在jvm源码中有一个数值来记录加锁和解锁的次数,所以我们是可以多次套用synchronized的
public void lockMethod(){
synchronized(obj){
synchronized(obj){
System.out.println(“我没报错”);
}
}
}
synchronized到底锁了什么
还是拿上个每次加锁的时候会在对象头内记录我们的加锁信息,我们这里来说一下对象头里面都放置了什么吧。
以32位JVM内部存储结构为例
由此看出对象一直是有一个位置来记录我们的锁信息的。说到这我们就可以来看一下我们锁的膨胀升级过程了。
锁的膨胀升级
我们说过了对象头的内容,接下来可以说说我们的锁内部是如何升级上锁的了。从无锁到重量级锁的一个升级过程,我们来边画图,边详细看一下。
无锁状态:
开始时应该这样的,线程A和线程B要去争抢锁对象,但还未开始争抢,锁对象的对象头是无锁的状态也就是25bit位存的hashCode,4bit位存的对象的分代年龄,1bit位记录是否为偏向锁,2bit位记录状态,优先看最后2bit位,是01,所以说我们的对象可能无锁或者偏向锁状态的,继续前移一个位置,有1bit专门记录是否为偏向锁的,1代表是偏向锁,0代表无锁,刚刚开始的时候一定是一个无锁的状态,这个不需要多做解释,系统不同内部bit位存的东西可能有略微差异,但关键信息是一致的。
偏向锁:
这时线程开始占有锁对象,比如线程A得到了锁对象。
就会变成这样的,线程A拿到锁对象,将我们的偏向锁标志位改为1,并且将原有的hashCode的位置变为23bit位存放线程A的线程ID(用CAS算法得到的线程A的ID),2bit位存epoch,偏向锁是永远不会被释放的。
接下来,线程B也开始运行,线程B也希望得到这把锁啊,于是线程B会检查23bit位存的是不是自己的线程ID,因为被线程A已经持有了,一定锁的23bit位一定不是线程B的线程ID了
然后线程B也会不甘示弱啊,会尝试修改一次23bit位的对象头存储,如果说这时恰好线程A释放了锁,可以修改成功,然后线程B就可以持有该偏向锁了。如果修改失败,开始升级锁。自己无法修改,线程B只能找“大哥”了,线程B会通知虚拟机撤销偏向锁,然后虚拟机会撤销偏向锁,并告知线程A到达安全点进行等待。线程A到达了安全点,会再次判断线程是否已经退出了同步块,如果退出了,将23bit位置空,这时锁不需要升级,线程B可以直接进行使用了,还是将23bit的null改为线程B的线程ID就可以了。
轻量级锁:
如果线程B没有拿到锁,我们就会升级到轻量级锁,首先会在线程A和线程B都开辟一块LockRecord空间,然后把锁对象复制一份到自己的LockRecord空间下,并且开辟一块owner空间留作执行锁使用,并且锁对象的前30bit位合并,等待线程A和线程B来修改指向自己的线程,假如线程A修改成功,则锁对象头的前30bit位会存线程A的LockRecord的内存地址,并且线程A的owner也会存一份锁对象的内存地址,形成一个双向指向的形式。而线程B修改失败,则进入一个自旋状态,就是持续来修改锁对象。
java架构之路(多线程)synchronized详解以及锁的膨胀升级过程相关推荐
- java架构升级_java架构之路(多线程)synchronized详解以及锁的膨胀升级过程
上几次博客,我们把volatile基本都说完了,剩下的还有我们的synchronized,还有我们的AQS,这次博客我来说一下synchronized的使用和原理. synchronized是jvm内 ...
- synchronized锁升级_synchronized详解以及锁的膨胀升级过程
点击上方"码之初"关注,···选择"设为星标" 与精品技术文章不期而遇 来源:www.cnblogs.com/cxiaocai/p/12189848.html ...
- 多线程——synchronized详解
多线程--synchronized详解 "当多个线程同时访问一个对象时,如果不用考虑这些线程在运行时环境下 的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用 ...
- java线程的cancel_多线程-Cancel详解
多线程-Cancel详解 在取消一个对等线程的请求被同意时,会有一个取消过程同pthread_cancel( )的返回异步发生. 目标线程的取消类型和取消状态决定了取消何时真正发生.可取消性状态描述了 ...
- java中提示peerbytes_Java中synchronized详解
synchronized官方解释 翻译成中文: Synchronized同步方法可以支持使用一种简单的策略来防止线程干扰和内存一致性错误:如果一个对象对多个线程可见,则对该对象变量的所有读取或写入都是 ...
- 一、Synchronized详解与锁升级
一.synchronized 1.共享问题 1.共享问题:在多线程的环境下,由于多个公共资源可能会被多个线程共享,也就是多个线程可能会操作(增.删.改等)同一资源.当多个线程操作同一资源时,很容易导致 ...
- 并发编程专题——第二章(并发编程之Synchronized详解)
日常中我们都会用到Synchronized关键字,但是面试就喜欢问这些,你说不重要吧,面试就不问了,你说重要吧,工作中除了高并发之外,很少能在业务代码中使用到的.所以笔者顶着风险,写下此篇对Synch ...
- 并发编程四 synchronized详解
一 设计同步器的意义 多线程编程中,有可能会出现多个线程同时访问同一个共享.可变资源的情况,这个资源我们称之其为临界资源:这种资源可能是:对象.变量.文件等. 共享:资源可以由多个线程同时访问 可变: ...
- synchronized的使用和底层原理、锁状态的膨胀升级过程
文章目录 1. synchronized介绍 2. synchronized底层原理 3. synchronized锁的膨胀升级过程 4. synchronized锁状态的记录位置 5. synchr ...
最新文章
- mysql检查备份数据脚本并在zabbix上告警
- 各种友(e)善(xin)数论总集,从入门到绝望2
- Rabbitmq如何保证消息顺序执行
- hdu 1788 Chinese remainder theorem again 【crt的具体过程】
- XML解析之dom4j
- python 抓取目录树_python 获取文件下所有文件或目录os.walk()的实例
- 允许活动内容如脚本和activex控件
- C\C++ bool、int、float和double、指针与0比较的方法
- 1640. Check Array Formation Through Concatenation 能否连接形成数组
- 1.rabbitmq 集群版安装及使用nginx进行四层负载均衡设置
- 通杀IIS7.0畸形解析0day漏洞
- 数据库笔记12:创建与管理触发器
- 漫画小说听书三合一分销平台源码
- ssh 命令连接服务器
- 【晶体管电路设计】四、共基极放大电路设计
- 基于springboot编写的小程序幸运转盘抽奖功能
- springboot的web进阶知识(2)
- marshmallow——快速入门
- Python中用PyPDF2拆分pdf提取页面
- 从资源爬取到个人微信公众号开发:使用 Python 打造公众号电影搜索器
热门文章
- elasticsearch获取一个字段的值_Elasticsearch,你觉得自己懂了多少,看看这篇文章吧...
- (48)FPGA状态机描述(一段式)
- (100)详细描述一个你做过的项目, 面试必问(二十四)(第20天)
- (21)FPGA移位寄存器设计(第5天)
- php凑整10算法,凑整法练习题.doc
- viper4android 脉冲样本,【图片】大福利,ViPER4Android FX音效及超过200个精选脉冲样本(转)【华为荣耀3x畅玩版吧】_百度贴吧...
- python3有什么用_Python 3.9的到来到底是意味着什么
- 202.linux系统相关函数
- 网络层(网际控制报文协议ICMP)
- 内核中的UDP socket流程(1)