Synchronized的锁优化

网友投稿 274 2022-09-21

Synchronized的锁优化

今天跟着CSDN的一篇blog来学习锁的四种优化

1. 锁消除

原理:JVM在JIT(即时编译)的时候,扫描上下文,去除掉那些不可能发生共享资源竞争的锁,从而而节省了线程请求这些锁的时间。 例子:StringBuffer的append方法是一个同步方法,如果StringBuffer类型的变量是一个局部变量,则该变量就不会被其它线程所使用,即对局部变量的操作是不会发生线程不安全的问题。 在这种情景下,JVM会在JIT编译时自动将append方法上的锁去掉。

2. 锁粗化

原理:将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁。 例子:在for循环里的加锁/解锁操作,一般需要放到for循环外。因为每次循环都要加锁不如一次性加完锁

3. 使用偏向锁和轻量级锁

说明: 1)java6为了减少经常获取和释放锁带来的开销,引入了轻量级锁和偏向锁 2)锁一共有4中状态,级别从低到高为:无锁状态、偏向级锁、轻量级锁、重量级锁 3)锁的状态会随着竞争的情况逐渐升级,而且只能升级不能降级。

【偏向锁】 概念:大多数情况下,锁不仅不会被多线程竞争,反而还会只被一个线程多次的获取,这个时候就存在偏向锁。 原理:当一个线程拿到当前锁后,锁会在对象头和栈帧中记录这个线程的ID,等到下次这个线程来获取锁时,不用进行CAS的加锁和解锁,只需查看Mark Word中是否存储指向当前线程的偏向锁。

偏向锁的获取: (1)访问对象的Mark Word中偏向锁的标志位是否为1,如果是1则为偏向锁 ①:如果偏向锁的标志位为0说明为无锁状态,线程通过CAS操作尝试获取偏向锁,如果获取锁成功,则将Mark Word的偏向线程ID设置成当前线程ID,并且标志位置1 ②如果标志位不为0也不为1说明存在锁竞争,偏向锁已经膨胀为轻量级锁,这时候通过CAS获取锁

(2)如果是偏向锁,则判断当前线程ID是否和Mark Word中的偏向线程ID一致,如果相同则无需进行CAS操作得到锁 (3)如果不一致则通过CAS操作获取锁,成功后修改MarkWord中的偏向线程ID指向这个线程ID。 (4)如果CAS获取偏向锁失败,则表示存在竞争。当达到安全局安全点时(在这个时间点没有正在执行的字节码),获得偏向锁的线程被挂起,偏向锁膨胀升级成轻量锁,然后被阻塞在安全点的线程进行往下执行同步代码。

偏向锁的释放: (1)当其他线程尝试获取偏向锁时,持有偏向锁的线程才会释放偏向锁 (2)释放偏向锁需要等待到全局安全点 (3)过程: ①若持有该锁的线程不活动了,则释放该线程的锁,将对象头设置成无锁状态 ②若持有锁的线程还在活动,则说明发生了竞争,挂起该进程,锁升级为轻量锁然后刚刚被暂停的线程继续执行该同步代码。 (4)优点:加锁和解锁不需要额外的消耗,和执行非同步代码块的效率仅存在纳秒级的差距 (5)缺点:如果存在线程竞争,锁撤销会带来多于的开销 (6)额外说明: ①偏向锁默认在应用程序启动几秒钟之后才激活。 ②可以通过设置 -XX:BiasedLockingStartupDelay=0 来关闭延迟。 ③可以通过设置 -XX:-UseBiasedLocking=false 来关闭偏向锁,程序默认会进入轻量级锁状态。(如果应用程序里的锁大多情况下处于竞争状态,则应该将偏向锁关闭)

【轻量级锁】 原理: ①当使用轻量级锁时(锁标识位为00),线程在执行同步代码之前,JVM会先在当前线程的栈帧中创建用于存储锁记录的空间,并将对象头中Mark Word复制到锁记录中(这个锁记录在栈帧中名为 Displaced Mark Word) ②将对象头的Mark Word复制到栈帧中的锁记录后,虚拟机将尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针,如果此时没有线程占用锁或者没有线程竞争锁,则当前线程获取到该锁,然后执行同步代码块。 ③如果在获取锁并且执行同步代码块的过程中,另外一个线程也完成了栈帧中锁记录的创建,并且已经将Mark Word复制到自己栈的锁记录中,然后尝试用CAS将对象头中的Mark Word修改为自己的锁记录指针,当时由于之前获取了轻量锁的线程已经修改过Mark Word了,所以此时对象头中的Mark Word与当前线程锁记录中的Mark Word值不相同,导致CAS失败。然后改线程就会不断的执行CAS操作去替换Mark Word中的值,(当循环次数或者循环时间达到了上限则停止)如果在结束之前CAS操作成功,则改线程获取该锁并且成功的修改了Mark Word中的值,如果修改失败,则对象头中的Mark Word的值会被修改成指向重量锁的指针,然后该线程挂起进入阻塞态。 ④当持有锁的那个进程执行完代码块后,使用CAS操作将对象头Mark Word还原为最初的状态时(将对象头中指向锁记录的指针替换为Displaced Mark Word),发现Mark Word已经被修改为指向重量锁的指针,因此CAS操作失败,该线程唤起挂起中的线程进行新一轮竞争,而此时,所有竞争失败的锁都会被阻塞,而不是自旋。

自旋锁的概念: (1)没有得到锁的线程将自己运行一段时间自循环,而不是挂起 (2)自旋的代价就是该线程一直占用着处理器,如果占有锁的线程执行代码时间越短,则自旋的效果就越好,反之开销很大 (3)所以自旋的等待时间必须要有一定的限度。

轻量锁的优点:在没有多线程的竞争下,可以减少传统重量锁带来的消耗 缺点:竞争失败的锁会进行自旋,消耗CPU 应用:追求响应时间,同步代码块执行速度要非常快。

【重量级锁】 说明: (1)java6之前的synchronized锁都是重量级锁,效率很低,因为monitor是依赖操作系统的Mutex Lock(互斥量)来实现的 (2)多线程竞争锁时,会引起线程的上下文切换(在时间片还没有用完的情况下进行了上下文切换),需要从用户态转化到核心态,这个状态切换需要很长的时间,所以时间成本很高 (3)在互斥的状态下,没有得到锁的线程将会被挂起,而挂起和恢复线程的操作都需要从用户态转化成内核态中完成。 优点:没有得到锁的线程不用自旋,直接挂起,不用消耗cpu 缺点:在大量竞争下,效率会异常低 应用:追求吞吐量,同步块执行速度较长。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:文案来啦:适合12月31日朋友圈跨年的文案句子!
下一篇:Anroid插件化/模块化/热修复知识梳理
相关文章

 发表评论

暂时没有评论,来抢沙发吧~