http://www.javashuo.com/article/p-eawupvdg-ba.htmljava
由轻量锁切换到重量锁,是发生在轻量锁释放锁的期间,以前在获取锁的时候它拷贝了锁对象头的markword,在释放锁的时候若是它发如今它持有锁的期间有其余线程来尝试获取锁了,而且该线程对markword作了修改,二者比对发现不一致,则切换到重量锁。github
由于重量级锁被修改了,因此display mark word和原来的markword不同了。多线程
怎么补救,就是进入mutex前,compare一下obj的markword状态。确认该markword是否被其余线程持有。jvm
此时若是线程已经释放了markword,那么经过CAS后就能够直接进入线程,无需进入mutex,就这个做用。spa
若是线程尝试获取锁的时候,轻量锁正被其余线程占有,那么它就会修改markword,修改重量级锁,表示该进入重量锁了。.net
还有一个注意点:等待轻量锁的线程不会阻塞,它会一直自旋等待锁,并如上所说修改markword。线程
这就是自旋锁,尝试获取锁的线程,在没有得到锁的时候,不被挂起,而转而去执行一个空循环,即自旋。在若干个自旋后,若是尚未得到锁,则才被挂起(进入阻塞状态),得到锁,则执行代码。指针
1. 检测Mark Word里面是否是当前线程的ID,若是是,表示当前线程处于偏向锁对象
2. 若是不是,则使用CAS将当前线程的ID替换Mard Word,若是成功则表示当前线程得到偏向锁,置偏向标志位1
3. 若是失败,则说明发生竞争,撤销偏向锁,进而升级为轻量级锁。
4. 当前线程使用CAS将对象头的Mark Word替换为锁记录指针,若是成功,当前线程得到锁
5. 若是失败,表示其余线程竞争锁,当前线程便尝试使用自旋来获取锁。
6. 若是自旋成功则依然处于轻量级状态。
7. 若是自旋失败,则升级为重量级锁。
上面几种锁都是JVM本身内部实现,当执行synchronized同步块的时候jvm会根据启用的锁和当前线程的争用状况,决定如何执行同步操做。
在全部的锁都启用的状况下线程进入临界区时会先去获取偏向锁,若是已经存在偏向锁了,则会尝试获取轻量级锁,启用自旋锁,若是自旋也没有获取到锁,则使用重量级锁,没有获取到锁的线程阻塞挂起,直到持有锁的线程执行完同步块唤醒他们
偏向锁是在无锁争用的状况下使用的,也就是同步开在当前线程没有执行完以前,没有其它线程会执行该同步块,一旦有了第二个线程的争用,偏向锁就会升级为轻量级锁,若是轻量级锁自旋到达阈值后,没有获取到锁,就会升级为重量级锁。
若是线程争用激烈,那么应该禁用偏向锁。
|
重量级锁 |
轻量级锁 |
偏向锁 |
适用场景 |
发生了锁争抢的状况:多条线程进入同步块并争用锁 |
虽然不少线程,可是没有冲突:多条线程进入同步块,可是线程进入时间错开于是并未争抢锁 |
自始至终只有一个线程:只有一个线程进入同步块 |
本质 |
互斥同步 |
CAS操做代替互斥同步 |
取消同步操做 |
优势 |
不会空耗CPU |
不会阻塞 |
不阻塞,执行效率高(只有第一次获取偏向锁时须要CAS操做,后面只是比对ThreadId) |
缺点 |
阻塞,上下文切换,重量级操做 |
长时间获取不到锁空耗CPU |
适用场景太局限。若竞争产生,会有额外的偏向锁撤销的消耗 |
不一样的锁有不一样特色,每种锁只有在其特定的场景下,才会有出色的表现,java中没有哪一种锁可以在全部状况下都能有出色的效率,引入这么多锁的缘由就是为了应对不一样的状况。