一、锁优化的思路和方法
锁优化是指:在多线程的并发中当用到锁时,尽可能让性能有所提高。一般并发中用到锁,就是阻塞的并发,前面讲到一般并发级别分为阻塞的和非阻塞的(非阻塞的包含:无障碍的,无等待的,无锁的等等),一旦用到锁,就是阻塞的,也就是一般最糟糕的并发,因此锁优化就是在堵塞的情况下去提高性能;所以所锁的优化就是让性能尽可能提高,不管怎么提高,堵塞的也没有无锁的并发底。让锁定的障碍降到最低,锁优化并不是说就能解决锁堵塞造成的性能问题。这是做不到的。
方法如下:
减少锁持有时间
减小锁粒度
锁分离
锁粗化
锁消除
二、减少锁持有时间
尽量不要把整个方法都加synchronized,只在方法中的某个需要用到锁的地方加synchronized(this){}
三、减小锁粒度
比如一个卫生间,一个人进去上厕所,上锁。如果把卫生间隔离成洗澡区,上厕所区,洗手区,进去上自己的锁,那么粒度就降低了
将大对象,拆成小对象,好处是:大大增加并行度,降低锁竞争(同时偏向锁,轻量级锁成功率提高)
提高偏向锁,轻量级锁成功率
HashMap的同步实现( HashMap他是非线程安全的实现)
– Collections.synchronizedMap(Map m)(多线程下使用时:用该synchronizedMap封装方式先封装让他实现线程同步的)
– 返回SynchronizedMap对象
内部实现如下:就是实现对set与get进行加锁,进行互斥上同步,不管读还是写都会拿到这个互斥对象。他变成很重的对象,不管读还是写,都会互斥阻塞,读堵塞写,写堵塞读,当多个读和写时线程会一个一个的进来。
ConcurrentHashMap(高性能的hash表,他就是做了减少锁粒度的实现,他被拆分好像16个Segment,每个Segment就是一个个小的hashmap.。就是把大的hash表拆成若干个小的hash表。)
– 若干个Segment :Segment[] segments
– Segment中维护HashEntry
– put操作时• 先定位到Segment,锁定一个Segment,执行put
在减小锁粒度后, ConcurrentHashMap允许若干个线程同时进入
五、锁分离
读写锁分离,读不改编数据,所以不堵塞,写才堵塞。
ReadWriteLock : 维护了一对锁,读锁可允许多个读线程并发使用,写锁是独占的。
每次只能有一个写线程,但是同时可以有多个线程并发地读数据。ReadWriteLock适用于读多写少的并发情况。
六、锁粗化
通常情况下,为了保证并发,要尽量少的减少锁持有时间,但如果一个方法不停地上锁解锁,那更影响效率,此时可以把所有synchronized全放到一个synchronized中,但前提是不需要上锁的代码要可以很快地执行完。
七、锁消除
在即时编译器时,如果发现不可能被共享的对象,则可以消除这些对象的锁操作。
有时候对完全不可能加锁的代码执行了锁操作,因为些锁并不是我们加的,是JDK的类引用进来的,当我们使用的时候,会自动引进来,所以我们会在不可能出现在多线程需要同步的情况就执行了锁操作。在某些条件成熟下,系统会消除这些锁。
八、虚拟机内的锁优化
他们不是Java语言层面的锁优化方法,是虚拟机层面的方法
内置于JVM中的获取锁的优化方法和获取锁的步骤
– 偏向锁可用会先尝试偏向锁
– 轻量级锁可用会先尝试轻量级锁
– 以上都失败,尝试自旋锁
– 再失败,尝试普通锁,使用OS互斥量在操作系统层挂起 OS互斥量:
(1)、偏向锁、轻量级锁、重量级锁适用于不同的并发场景:
- 偏向锁:无实际竞争,且将来只有第一个申请锁的线程会使用锁。
- 轻量级锁:无实际竞争,多个线程交替使用锁;允许短时间的锁竞争。
- 重量级锁:有实际竞争,且锁竞争时间长。
另外,如果锁竞争时间短,可以使用自旋锁进一步优化轻量级锁、重量级锁的性能,减少线程切换。
如果锁竞争程度逐渐提高(缓慢),那么从偏向锁逐步膨胀到重量锁,能够提高系统的整体性能。
--------------------- 本文来自 平凡之路无尽路 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/gududedabai/article/details/80911855?utm_source=copy