Synchronized原理

目录

一.Synchronized锁的状态

二.偏向锁

1.加锁过程

2.解锁过程

三.轻量级锁

1.加锁过程

2.解锁过程

四.重量级锁

五.总结


一.Synchronized锁的状态

四种锁状态:

        1.无锁

        2.偏向锁

        3.轻量级锁

        4.重量级锁

锁的级别由低到高分别是:无锁状态->偏向锁状态->轻量级锁状态->重量级锁状态。这几个状态会随着锁的竞争情况逐渐升级(只能升级,不能降级),也叫锁的膨胀。

二.偏向锁

作用:减少同一线程获取锁的代价

        偏向锁假定将来只有第一个申请锁的线程会使用锁(不会有任何线程再来申请锁),因此,只需要在Mark Word中CAS记录owner(本质上也是更新,但初始值为空),如果记录成功,则偏向锁获取成功,记录锁状态为偏向锁,以后当前线程等于owner就可以零成本的直接获得锁;否则,说明有其他线程竞争,膨胀为轻量级锁。

1.加锁过程

        当锁对象第一次被线程获取的时候,虚拟机把对象头中的锁标志位设为“01”,即偏向模式。同时使用CAS操作把获取到这个锁的线程的ID记录在对象的Mark Word之中的偏向线程ID,并将是否偏向锁的状态位置置为1。如果CAS操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时,直接检查markword中的ThreadId是否和 自身线程Id一致,如果一致,则认为当前线程已经获取了锁,JVM就可以不再进行任何同步操作。

        当有另外一个线程去尝试获取这个锁时,偏向模式就宣告结束。根据锁对象目前是否处于被锁定的状态,撤销偏向(Revoke Bias)后恢复到未锁定状态或 轻量级锁定(标志位为“00”)的状态。

2.解锁过程

        一旦有第二个线程访问这个对象,因为偏向锁不会主动释放,所以第二个线程可以看到对象是偏向状态,这时表明在这个对象上已经存在竞争了,检查原来持有该对象锁的线程是否依然存活,如果挂了,则可以将对象变为无锁状态,然后重新偏向新的线程,如果原来的线程依然存活,则马上执行那个线程的操作栈,检查该对象(锁)的使用情况,如果仍然需要持有偏向锁,则偏向锁升级为轻量级锁,(偏向锁就是这个时候升级为轻量级锁的)。如果不存在使用了,则可以将对象(锁)回复成无锁状态,然后重新偏向。

三.轻量级锁

        当有别的线程参与到偏向锁的竞争中时,会先判断 markword 中的线程ID与这个线程是否一致,如果不一致,则会立即撤销偏向锁,升级为轻量级锁。

        每个线程都会在自己的栈中维护一个 LockRecord(LR),然后每个线程在竞争锁时,都试图将 锁对象头 中的markword 设置为指向自己LR的指针,哪个线程设置成功,则意味着哪个线程成功获取到锁。

1.加锁过程

        在代码进入同步块前,如果该同步块没有被锁定(即锁的标志位为“01”),那么JVM在将当前线程的栈帧中,创建一个 LockRecord(锁记录LR),并将锁对象头中的 markWord 信息复制到锁记录(LR)中,这个官方称为 Displaced Mard Word。
然后线程尝试使用 CAS 将对象头中的 MarkWord 替换为指向锁记录(LR)的指针。

如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位(Mark Word的最后2bit)将转变为“00”,即表示此对象处于轻量级锁定状态。

如果 cas 失败,有两种情况:
        如果是其它线程已经持有了该 Object 的轻量级锁,这时表明有竞争,当前线程便尝试使用自旋来获取锁。如果在自旋一定次数后仍未获得锁,那么轻量级锁将会升级成重量级锁。
        如果是自己执行了 synchronized 锁重入,那么再添加一条 Lock Record 作为重入的计数

2.解锁过程

        当退出 synchronized 代码块(解锁时)如果有取值为 null 的锁记录,表示有重入,这时重置锁记录,表示重入计数减一。
        当退出 synchronized 代码块(解锁时)锁记录的值不为 null ,这时使用 cas Mark Word 的值恢复给对象头。
        成功,则解锁成功
        失败,说明轻量级锁进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程

四.重量级锁

        如果在尝试加轻量级锁的过程中,CAS 操作无法成功,这时一种情况就是有其它线程为此对象加上了轻量级锁(有竞争),这时需要进行锁膨胀,将轻量级锁变为重量级锁。
这时 Thread-1 加轻量级锁失败,进入锁膨胀流程:
        即为 Object 对象申请 Monitor 锁,让 Object 指向重量级锁地址
        然后自己进入 Monitor EntryList BLOCKED
        当 Thread-0 退出同步块解锁时,使用 cas Mark Word 的值恢复给对象头,失败。这时会进入重量级解锁流程,即按照 Monitor 地址找到 Monitor 对象,设置 Owner null ,唤醒 EntryList BLOCKED 线程。

五.总结

由于轻量级锁会自旋,即不会放弃CPU,那么对一些执行时间短的任务而言,用轻量级锁可以减少线程切换的时间(对比重量级锁)。

(1)偏向锁通过对比 Mark Word 解决加锁问题,避免执行CAS操作。
(2)轻量级锁是通过用 CAS 操作和自旋来解决加锁问题,避免线程阻塞和唤醒而影响性能。
(3)重量级锁是将除了拥有锁的线程以外的线程都阻塞。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值