Synchronized 总结

在Java中,为了保证多线程共享资源的原子性,引入了synchronized关键字,但在JDK1.5之前,被实现为重量级锁。

重量级锁

每次进入synchronized代码块时,JVM会通过monitorenter和monitorexit指令来加锁和释放锁。

JVM会为每个被锁住的对象创建一个对应的ObjectMonitor对象,用于管理竞争线程的挂机和唤醒。

当存在多个线程竞争同一个锁时,未抢到锁的线程会被挂起(Block),进入阻塞状态,等待被唤醒。

线程的挂起和唤醒依赖于操作系统的调度机制,这些涉及到用户态和内核态之间的切换,本质上是系统调用(syscall),开销大。

锁优化

JDK1.6引入了锁优化 - 锁升级机制

HotSpot JVM为synchronized引入了锁升级机制,形成了

        无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁    的锁膨胀过程。

  • 无锁状态

对象没有被任何线程持有。

  • 偏向锁

当一个线程T1第一次访问某个synchronized区块:

1,如果对象处于无锁状态,Mark Word会写入当前线程(Current Thread Id)

2,设置锁标志为“偏向锁”

3,之后如果是T1线程访问这把锁,就可以直接通过,性能极高。

      偏向锁延迟启动机制:

JVM为了避免JVM启动阶段的类初始化中出现大量无意义的偏向锁 + 撤销 + 升级行为。

JVM默认在启动4秒后,才启用偏向锁,这个可以由参数控制。

  • 轻量级锁

当线程T2抢锁时,偏向锁会被撤销,并升级为轻量级锁,流程如下:

1,T2线程栈中创建一个Lock Record(锁记录)

2,将对象的Mark Word原始内容(包括T1的信息)拷贝到Lock Record。

3,然后T2使用CAS操作,尝试将对象的Mark Word替换为Lock Record的地址,同时设置锁标志为轻量级锁。在轻量级锁中,没有ThreadId了,以锁指针值指向的线程为Owner。

        相当于,把锁放到了自己栈中,如果成功,说明当前对象锁已被T2拿到,如果失败,说明有竞争,T2会尝试自旋等于锁释放。

        自旋锁:

自旋就是T2不挂起,而是在CPU上loop若干次,重复尝试CAS,这个次数不是固定的,大致范围在10次以内,也可由JVM参数控制。

自旋成功:拿到锁。

自旋失败:自旋次数达到上限,升级为重量级锁。

  • 重量级锁

此时JVM会将未拿到锁的线程加入ObjectMonitor的waitSet中。

线程被挂起,等待唤醒,这里是涉及到真正的OS阻塞了,会有很大性能开销。

        锁升级是单向的,不可降级:

1,一旦锁升级,就不会降级。

2,锁只能从无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁 

3,唯一的重置方式是当前资源变成了无锁状态。如果后续有线程加锁,才会重新走锁升级的流程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值