看以下代码java
public class Test { StringBuffer stb = new StringBuffer(); public void test1(){ //jvm的优化,锁的粗化 stb.append("1"); stb.append("2"); stb.append("3"); stb.append("4"); }
首先咱们要清除StringBuffer是线程安全的,由于它在每个方法上都加了synchronized锁,下图是StringBuffer的源码安全
按照正常的理解synchronized是对当前对象加锁,那么咱们调用了四次append方法,那么jvm是将这把对象锁加了四次吗?以下图:多线程
那这样的化,jvm就须要加四次锁,固然也要释放四次锁,频繁加解锁引发线程上下文的切换,很是消耗性能,因此jvm作了优化,只加一次锁,叫作锁的粗化,能够理解为将锁的颗粒度放大app
如图看下面代码jvm
public void test2(){ //jvm的优化,JVM不会对同步块进行加锁 synchronized (new Object()) { //伪代码:不少逻辑 //jvm是否会加锁? //jvm会进行逃逸分析 } }
这个地方加锁等于没有加锁,由于每一个线程都会new object,你们都不会用同一把锁,jvm分析优化后不会对这种代码加锁(逃逸分析),因此,咱们平时加锁必定要注意,加锁要加同一把锁。性能
synchronized的锁的状态总共有四种,无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁能够从偏向锁升级到轻量级锁,再升级的重量级锁,可是锁的升级是单向的,也就是说只能从低到高升级,锁状态的升级不可逆。优化
JDK1.6版本以后对synchronized的实现进行了各类优化,如自旋锁、偏向锁和轻量级锁 并默认开启偏向锁 开启偏向锁:-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0 关闭偏向锁:-XX:-UseBiasedLocking。若是直接上来就是重量级锁,那么实在是太消耗资源了。spa
注意一下几点:操作系统
线程1获取轻量级锁后会将Object Mark Word 复制本身的一份到本身的栈空间,而后在本身的栈空间开辟一个指针lockerecord 指向Object Mark Word,同时Object Mark Word也会指向lockerecord,当线程1执行完代码块释放轻量级锁以后,发现Object Mark Word不在指向本身,说明当前锁已经改成重量级锁,那么它会唤醒阻塞队列中全部线程从新竞争锁。线程
总结:偏向锁,轻量级锁都是基于Object Mark Word的标记实现,java尽量避免使用重量级锁。