synchronized作为Java程序员最常用同步工具,很多人却对它的用法和实现原理一知半解,以至于还有不少人认为synchronized是重量级锁,性能较差,尽量少用 。
但不可否认的是synchronized依然是并发首选工具,连volatile、CAS、ReentrantLock都无法动摇synchronized的地位 。synchronized是工作面试中的必备技能,今天就跟着一灯一块深入剖析synchronized底层到底做了哪些优化?
synchronized是用来加锁的,而锁是加在对象上面,所以需要先聊一下JVM中对象构成 。
1. 对象的构成Java对象在JVM内存中由三块区域组成:对象头、实例数据和对齐填充 。
对象头又分为:Mark Word(标记字段)、Class Pointer(类型指针)、数组长度(如果是数组) 。
实例数据是对象实际有效信息,包括本类信息和父类信息等 。
对齐填充没有特殊含义,由于虚拟机要求 对象起始地址必须是8字节的整数倍,作用仅是字节对齐 。
Class Pointer是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例 。
重点关注一下对象头中Mark Word,里面存储了对象的hashcode、锁状态标识、持有锁的线程id、GC分代年龄等 。
在32为的虚拟机中,Mark Word的组成如下:
文章插图
2. synchronized锁优化从JDK1.6开始,就对synchronized的实现机制进行了较大调整,包括使用JDK1.5引进的CAS自旋之外,还增加了自适应的CAS自旋、锁消除、锁粗化、偏向锁、轻量级锁等优化策略 。由于使得synchronized性能极大提高,同时语义清晰、操作简单、无需手动关闭,所以推荐在允许的情况下尽量使用此关键字,同时在性能上此关键字还有优化的空间 。
【再有人说synchronized是重量级锁,就把这篇文章扔给他看】锁主要存在四种状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,性能依次是从高到低 。锁可以从偏向锁升级到轻量级锁,再升级的重量级锁 。但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级 。
在 JDK 1.6 中默认是开启偏向锁和轻量级锁的,可以通过-XX:-UseBiasedLocking来禁用偏向锁 。2.1 自旋锁线程的挂起与恢复需要CPU从用户态转为内核态,频繁的阻塞和唤醒对CPU来说是一件负担很重的工作,势必会给系统的并发性能带来很大的压力 。同时我们发现在许多应用上面,对象锁的锁状态只会持续很短一段时间,为了这一段很短的时间频繁地阻塞和唤醒线程是非常不值得的 。
自旋锁就是指当一个线程尝试获取某个锁时,如果该锁已被其他线程占用,就一直循环检测锁是否被释放,而不是进入线程挂起或睡眠状态 。自旋锁适用于锁保护的临界区很小的情况,临界区很小的话,锁占用的时间就很短 。自旋等待不能替代阻塞,虽然它可以避免线程切换带来的开销,但是它占用了CPU处理器的时间 。如果持有锁的线程很快就释放了锁,那么自旋的效率就非常好,反之,自旋的线程就会白白消耗掉处理的资源,它不会做任何有意义的工作,这样反而会带来性能上的浪费 。所以说,自旋等待的时间(自旋的次数)必须要有一个限度,如果自旋超过了定义的时间仍然没有获取到锁,则应该被挂起 。
自旋锁在JDK 1.4.2中引入,默认关闭,但是可以使用-XX:+UseSpinning开开启,在JDK1.6中默认开启 。同时自旋的默认次数为10次,可以通过参数-XX:PreBlockSpin来调整 。
2.2 自适应自旋锁JDK 1.6引入了更加智能的自旋锁,即自适应自旋锁 。自适应就意味着自旋的次数不再是固定的,它是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定 。那它如何进行适应性自旋呢?
经验总结扩展阅读
- 那时有人说我好像根本不懂浪漫是什么歌
- Java程序员必会Synchronized底层原理剖析
- 回门酒敬酒新人说些啥 真诚表达心意
- 金钱关系处理得很好的星座情侣
- 交往中的十二星座最不想听恋人说什么话
- 别人说端午安康怎么回复
- 男人说怎么了代表什么(男生说怎么了什么意思)
- 关于大象的成语有哪些
- exo最新签名粉丝必备 exo应援口号签名
- 鹦鹉为什么能模仿人说话