JVM的锁(2) - 锁优化

3 篇文章 0 订阅

一,自旋锁

互斥同步对性能最大的影响是阻塞的实现,挂起线程和恢复线程的操作都需要转入内核态中完成,这些操作给系统的并发性能带来了很大的压力。而在很多应用上,共享数据的锁定状态只会持续很短的一段时间。若实体机上有多个处理器,能让两个以上的线程同时并行执行,我们就可以让后面请求锁的那个线程原地自旋(不放弃CPU时间),看看持有锁的线程是否很快就会释放锁。为了让线程等待,我们只须让线程执行一个忙循环(自旋),这项技术就是自旋锁。

如果锁长时间被占用,则浪费处理器资源,因此自旋等待的时间必须要有一定的限度,如果自旋超过了限定的次数仍然没有成功获得锁,就应当使用传统的方式去挂起线程了(默认10次)。

JDK1.6引入自适应的自旋锁:自旋时间不再固定,由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,进而它将允许自旋等待持续相对更长的时间。

二,锁削除(同步消除)

锁削除是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行削除(主要判定依据来源于逃逸分析的数据支持,如果判断到一段代码中,在堆上的所有数据都不会逃逸出去被其他线程访问到,那就可以把它们当作栈上数据对待,认为它们是线程私有的,同步加锁自然就无须进行)。

看下面这几行代码:

public void add(String str1, String str2) {
    StringBuffer sb = new StringBuffer();
    sb.append(str1).append(str2);
}

众所周知,StringBuffer 的 append 方法是同步方法,但是在这个 add 方法中,StringBuffer 不会存在共享资源竞争的情况,因为其他线程并不会访问到它。这就符合了 “代码上要求同步,但不可能存在共享数据竞争” 的条件。因此虽然这里有锁,但是可以安全地清除掉,避免了锁的获取释放带来的性能消耗。

 

三,锁膨胀(锁粗化)

如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作是出现在循环体中的,那即使没有线程竞争,频繁地进行互斥同步操作也会导致不必要的性能损耗。 如果虚拟机探测到有这样一串零碎的操作都对同一个对象加锁,将会把加锁同步的范围扩展(膨胀)到整个操作序列的外部(由多次加锁编程只加锁一次)。

看下面这行代码:

public void add(String str1, String str2, String str3) {
    StringBuffer sb = new StringBuffer();
    sb.append(str1);
    sb.append(str2);
    sb.append(str3);
}

同样是 StringBuffer ,JVM 检测到有一连串操作都对同一个对象(sb)加锁时,就会把锁进行粗化处理,扩展同步范围,这样从一个 append() 到最后一个,只需要加一次锁就可以了。

 

更多锁优化相关知识点,请看我另一篇文章《JIT即时编译与编译优化》

 

 

 

 

 

 

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
JVM优化是为了提高多线程程序的性能和并发度。其中膨胀是指当一个线程获取失败时,JVM会将其自旋一定次数,如果还没有获得,就会将膨胀升级。 膨胀的过程一般分为以下三个阶段: 1. 自旋(Spin Locking):当一个线程获取失败时,JVM会将其自旋一定次数,尝试获取。自旋的目的是为了减少线程切换的开销,因为线程进入自旋状态时不会释放CPU资源。如果自旋次数超过了阈值,那么就会进入下一个阶段。 2. 轻量级(Lightweight Locking):在这个阶段,JVM会为争用的线程在对象头上分配一些空间,用于存储记录。这个记录包含了的指针、持有的线程ID以及一些标志位等信息。如果记录的CAS操作成功,那么当前线程就获得了。如果CAS操作失败,那么就会进入下一个阶段。 3. 重量级(Heavyweight Locking):在这个阶段,JVM会将升级为重量级,也就是使用操作系统提供的互斥(Mutex)来保证线程的安全性。重量级的代价很高,因为它会涉及到用户态和内核态之间的切换,所以尽量避免的膨胀。 的膨胀过程可以通过JVM参数来调节,例如可以设置自旋次数、启用偏向等。在实际应用中,应该尽量避免的竞争,采用分离、读写、无编程等技术来提高程序的并发度和性能。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值