JDK1.6之前,Synchrnized使用重量级锁,加锁及解锁消耗性能,导致效率低。
JDK1.6以后,Synchrnized进行了优化,升级过程为:无锁->偏向锁->轻量级锁->重量级锁。
锁对象的对象头中存储Markword,记录hashcode、分代年龄、锁标志等。
1、无锁-->偏向锁:
加锁:线程访问同步代码块,核实锁标志为01后,查看Markword中是否存储本线程ID,没有则通过CAS替换Markword,成功则升级为偏向锁,失败则说明存在多线程竞争,升级为轻量级锁。
撤销:同步代码块执行完后,并不会进行偏向锁撤销,继续保存线程ID,相同线程再次进入同步代码,仅需判断线程ID即可执行同步代码,省去申请锁的操作,提高性能。
2、偏向锁<-->轻量级锁:
加锁:当一个线程获得了偏向锁,并且该线程仍在同步块中执行,当有另一个线程尝试获得偏向锁,该偏向锁就会升级为轻量级锁。
会在栈中创建lock recored(锁记录),存放复制的Markword和指向锁对象的指针。然后通过CAS尝试修改Markword为指向锁记录的指针,成功则升级为轻量级锁,失败先判断Markword存放的指向是否指向本线程栈的锁记录,指向说明已获得锁,未指向说明存在多线程竞争,升级为重量级锁。
撤销:CAS尝试替换Markword为锁记录复制的Markword,成功则同步块执行完成,失败说明存在多线程竞争,升级为重量级锁。
3、轻量级锁升级为重量级锁:
加锁:轻量级锁CAS替换Markword失败则先进行自旋,自旋一定程度(和JVM、操作系统相关)仍未获得锁,表示自选失败,本线程阻塞,锁升级为重量级锁。
Markword存放指向重量级锁(monitor对象)的指针。
偏向锁失效:
当一个对象当前正处于偏向锁状态,并且在计算其identity hash code时(调用未覆盖的Object.hashCode()方法或者System.identityHashCode(Object o)),则它的偏向锁会被撤销,并且锁会膨胀为轻量级锁或者重量锁。
当一个对象已经计算过identity hash code,它就无法进入偏向锁状态。
* 偏向锁会使用Thread ID和epoch覆盖identity hash code。
* 轻量级锁的实现中,会通过线程栈帧的锁记录存储Displaced Mark Word。
* 重量锁的实现中,ObjectMonitor类里有字段可以记录非加锁状态下的mark word,其中可以存储identity hash code的值。