Java多线程13-性能调优

143 阅读3分钟
原文链接: mp.weixin.qq.com

一、Java虚拟机对内部锁的优化

a、锁消除:JIT编译器在动态编译同步块时,借助逃逸分析的技术来判断所使用的锁对象是否只能够被一个线程访问而没有被发布到其他线程;如果被证实只能够被一个线程访问,则编译时不生成synchronized所对应的机器码。

b、锁粗化: 对于相邻的几个同步块,如果这些同步块使用的是同一个锁实例,JIT编译器会将这些同步块合并为一个大同步块,从而避免了一个线程反复申请、释放同一个锁所导致的开销

c、偏向锁:基于观测结果:大多数锁没有被争用,并且这些锁在整个生命周期内至多只会被一个线程持有;因此Java虚拟机会为每个对象维护一个偏好(Bias),即一个对象对应的内部锁第一次被一个线程获得,那么这个线程就会被记为该对象的偏好线程(Biased Thread),这个线程后续无论再次申请该锁还是释放该锁,都无须借助原子操作,从而减少了锁的申请和释放。

d、适应性锁:存在锁争用的情况下,一个线程申请一个锁的时候如果这个锁恰好被其他线程持有,那么这个线程就需要等待该锁被其持有的线程释放;实现等待的一种保守方法:将线程暂停,但导致上下文切换;这种实现策略比较适合绝大多数线程对该锁持有时间较长场景。另一种采用忙等:不停的空循环等待

 

二、优化对锁的使用

锁的开销与锁的监视争用

a、上下文切换与线程调度开销

b、内存同步、编译器优化受限的开销:内存屏障所导致的冲刷写缓冲器、清空无效化队列所导致的开销;另外内存屏障阻止某些编译器优化。

c、限制可伸缩性:局部的将并发计算改为串行计算。

使用可参数化锁

减少临界区长度:减少锁被持有的时间从而降低锁被争用的概率

减少锁的粒度:降低锁的申请频率,从而减少锁被争用的概率;常见方法就是将一个粒度较粗的锁拆分若干粒度更细的锁

锁分段:对同一个数据结构内不同部分的数据使用不同锁实例进行加锁的技术

ConcurrentHashMap内部就使用了锁分段技术

 

考虑锁的替代品:volatile、原子变量、无状态对象、不可变对象、线程特有对象

 

三、减少系统内耗:上下文切换

控制线程数量

避免在临界区中执行阻塞式I/O等阻塞操作

避免在临界区中执行比较耗时操作

减少Java虚拟机的垃圾回收

 

四、性能的隐形杀手:伪共享

伪共享:由于一个缓存行中可以存储多个变量的副本,因此即便是两个线程各自访问各自的共享变量的情况下,一个线程更新其共享变量也可能导致另外一个线程访问其共享变量时产生缓存未命中