◆
锁优化策略
◆
1. 减少锁持有时间
对一个方法加锁,不如只对需要同步的那几行代码加锁,减少线程持有锁的时间。
2. 减小锁粒度
将大对象,拆成小对象进行加锁。例如ConcurrentHashMap采取对segment加锁而不是对整个map加锁,这样只要修改的segment不同,就可以并发的进行。
3. 锁分离
使用我们文章开头提到的文章中所说的读写分离锁或者根据不同的功能进行加锁
4. 锁粗化
策略1要求每个线程持有锁的时间越短越好,但是呢,锁的申请、同步和释放也是需要时间的,所以说如果有频繁的加锁请求时(例如在循环内加锁),最好把多个加锁的请求合并为一个(在循环外加锁),以减少锁的请求及释放的时间。
◆
虚拟机对锁优化做的努力
◆
1. 锁偏向
当一个线程获得锁之后,锁就会进入偏向模式。接下来这个线程如果再次请求锁的时候就不需要再做同步操作了。(即:持有该锁的线程在接下来的执行中遇到同步块时不再需要lock和unlock了,直接执行即可)直到另外的线程前来请求的时候锁偏向才会失效。
锁偏向的优化适用于锁竞争不是很强的场景,如果是竞争激烈的场景则偏向锁会一直处于失效状态。
2. 轻量级锁
如果偏向锁失败,那么系统会进行轻量级锁的操作,使用CAS操作来尝试加锁。如果轻量级锁失败,才调用系统级别的重量级锁(syncrhoized)来加锁。
3. 自旋锁
为了避免使用重量级锁的巨大消耗,虚拟机在轻量级锁失败膨胀后让当前线程做几个空循环(这就是自旋的来源)来赌这个线程可以在自旋之后获得锁,如果还不能获得锁的话则真实的使用重量级锁了。
4. 锁消除
虚拟机在编译时,会对上下文进行扫描,去除那些不可能存在竞争的锁。
总结
为了尽量避免使用重量级锁,JVM首先在编译期先消除一些不可能存在竞争的锁。当一个线程获得锁后会对这个线程偏向以保证此线程再次获得锁时可以减少同步所需的时间。偏向锁失败后会尝试轻量级锁,轻量级锁尝试使用CAS操作来获得锁。如果轻量级锁失败,JVM同样会赌一下此线程很可能很快获得锁,就会尝试自旋锁,将线程做几个空循环。如果自旋锁也失败,那么就只能升级成重量级锁了。
![640?wx_fmt=gif](https://i-blog.csdnimg.cn/blog_migrate/7ddfb123cd86f3f4acc3595a167ba52a.gif)
收藏转发好看再走呗!
![640?wx_fmt=gif](https://i-blog.csdnimg.cn/blog_migrate/4e1cc7f5ce51f78d5c1be8d3853fa54c.gif)