Synchronized
2021-10-26 15:59:34 14 举报
java锁
作者其他创作
大纲/内容
Thread-2
堆内存
4.如果 cas 替换成功,对象头中存储了 锁记录地址和状态 00 ,表示由该线程给对象加锁,这时图示如下
m2内调用synchronized(obj)
Thread-0
BLOCKED
Monitor 地址
EntryList
生成锁记录
Lock Record
1.轻量级锁在没有竞争时(就自己这个线程),每次重入仍然需要执行 CAS 操作。 Java 6 中引入了偏向锁来做进一步优化:只有第一次使用 CAS 将线程 ID 设置到对象的 Mark Word 头,之后发现这个线程 ID 是自己的就表示没有竞争,不用重新 CAS。以后只要不发生竞争,这个对象就归该线程所有
Lock record 地址 00
1.当 Thread-1 进行轻量级加锁时,Thread-0 已经对该对象加了轻量级锁
轻量级锁
Object
Kclass Word
HashCode Age Bias 01
用 ThreadID 替换 markword
当 Thread-0 退出同步块解锁时,使用 cas 将 Mark Word 的值恢复给对象头,失败。这时会进入重量级解锁 流程,即按照 Monitor 地址找到 Monitor 对象,设置 Owner 为 null,唤醒 EntryList 中 BLOCKED 线程
WaitSet
Thread-5
static final Object obj = new Object(); public static void method1() { synchronized( obj ) { // 同步块 A method2();} }public static void method2() { synchronized( obj ) { // 同步块 B}}
static final Object obj = new Object(); public static void m1() { synchronized( obj ) { // 同步块 A m2();}}public static void m2() { synchronized( obj ) { // 同步块 B m3();} }public static void m3() {synchronized( obj ) {//同步块C}}
Object Body
这时 Thread-1 加轻量级锁失败,进入锁膨胀流程 即为 Object 对象申请 Monitor 锁,让 Object 指向重量级锁地址然后自己进入 Monitor 的 EntryList BLOCKED
用锁记录替换Mark Word
偏向锁
m3内调用synchronized(obj)
1.刚开始 Monitor 中 Owner 为 null 2.当 Thread-2 执行 synchronized(obj) 就会将 Monitor 的所有者 Owner 置为 Thread-2,Monitor中只能有一 个 Owner 3.在 Thread-2 上锁的过程中,如果 Thread-3,Thread-4,Thread-5 也来执行 synchronized(obj),就会进入 EntryList BLOCKED4.Thread-2 执行完同步代码块的内容,然后唤醒 EntryList 中等待的线程来竞争锁,竞争的时是非公平的 5.图中 WaitSet 中的 Thread-0,Thread-1 是之前获得过锁,但条件不满足进入 WAITING 状态的线程,后面讲wait-notify 时会分析注意:synchronized 必须是进入同一个对象的 monitor 才有上述的效果不加 synchronized 的对象不会关联监视器,不遵从以上规则
Thread-1
Object reference
5.如果 cas 失败,有两种情况 如果是其它线程已经持有了该 Object 的轻量级锁,这时表明有竞争,进入锁膨胀过程如果是自己执行了 synchronized 锁重入,那么再添加一条 Lock Record 作为重入的计数
检查 ThreadID 是否是自己
HashCode Age Bias 01
对象
7.当退出 synchronized 代码块(解锁时)锁记录的值不为 null,这时使用 cas 将 Mark Word 的值恢复给对象 头成功,则解锁成功失败,说明轻量级锁进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程
Montior
Thread-3
m1内调用synchronized(obj)
由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐,这点了解即可。
自旋锁重量级锁竞争的时候,还可以使用自旋来进行优化,如果当前线程自旋成功(即这时候持锁线程已经退出了同步 块,释放了锁),这时当前线程就可以避免阻塞。 当一个线程正在占用着monitor锁,另一个线程过来准备占用这个锁时发现已经有线程正在占用着,就会通过自旋来等待占用的线程结束,如果占用的线程结束,那么会直接占用这把锁,如果自旋很多次都没有释放锁,则会进入阻塞状态自旋会占用 CPU 时间,单核 CPU 自旋就是浪费,多核 CPU 自旋才能发挥优势。 在 Java 6 之后自旋锁是自适应的,比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会 高,就多自旋几次;反之,就少自旋甚至不自旋,总之,比较智能。Java 7 之后不能控制是否开启自旋功能
3.让锁记录中 Object reference 指向锁对象,并尝试用 cas 替换 Object 的 Mark Word,将 Mark Word 的值存 入锁记录
Owner
实例对象
6.当退出 synchronized 代码块(解锁时)如果有取值为 null 的锁记录,表示有重入,这时重置锁记录,表示重 入计数减一
例子:
Monitor 结构
2.创建锁记录(Lock Record)对象,每个线程都的栈帧都会包含一个锁记录的结构,内部可以存储锁定对象的 Mark Word
锁膨胀
Moniter
Thread-4
1.假设有两个方法同步块,利用同一个对象加锁
对象头实例变量填充数据
重量级锁
存放类的属性数据信息,包括父类的属性信息,如果是数组的实例部分还包括数组的长度,这部分内存按4字节对齐。
收藏
0 条评论
回复 删除
下一页