假如对象是非数组类型,则没有 Array Length这一项。
从上往下分别是 无锁,偏向锁,轻量锁,重量锁,GC标记。
偏向锁的目标是减少昂贵的原子指令CAS(Compare And Swap)等的使用(相比轻量锁减少了CAS,当然更减少了os的介入)。Monitor里面也有可重入机制,就不会重新将该对象对应的Monitor里面的Owner改变了。(个人认为:这里应该不涉及到互斥信号量的分配问题,就算是 不是同一个线程竞争到了锁也不会重新分配mutex呀,这个对象对应的monitor早都分配好了呀不需重新分配。)
如若默认情况下偏向锁开启被延迟的话,-XX:BiasedLockingStartupDelay=0 可关于延迟。 如果确定应用程序里所有的锁通常情况下处于竞争状态,可以通过JVM参数关闭偏向锁。 (毕竟。。。。。。。。。。。。。。。。。。。。。。。)
%%%%%%%%%%%%%% 假如开启了偏向锁,当一个线程访问同步块并获取锁时,会在对象头存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁:
biased_lock=1。
刚开始无锁(这个无锁指的是没被任何线程进入过,不是不具备偏向锁,偏向锁不是一个真正的锁,就是代码层面去控制它而已),假如一个线程需要访问该同步块。则:
这个时候对象头的MarkWord(threId=null)没有存储着当前线程的偏向锁,通过CAS将对象头偏向锁的线程ID指向当前线程来竞争锁,变为: 这次CAS也可能竞争失败,也许被其他线程抢先了。(T__T)
一个线程在执行完同步代码块以后, 并不会尝试将 MarkWord 中的 thread ID 赋回原来的值 。这样做的好处是: 如果该线程需要再次对这个对象加锁,而这个对象之前一直没有被其他线程尝试获取过锁,依旧停留在可偏向的状态下, 即可在不修改对象头的情况下(即不使用CAS), 直接认为偏向成功。(只要threadID有值,且epoch不过期,CAS就一定会失败) 如果 CAS 操作失败, 则说明, 有另外一个线程 Thread B 抢先获取了偏向锁。 这种状态说明该对象的竞争比较激烈, 此时需要撤销 Thread B 获得的偏向锁,将 Thread B 持有的锁升级为轻量级锁(也可能重偏向)。 该操作需要等待全局安全点 JVM safepoint ( 此时间点, 没有线程在执行字节码) 。%%%%%%%%%%%%%%%%%
注释代码开启空线程后,线程id不同,不会被复用。而打印结果如下:(只是一种现象,没去看底层源码)
-XX:+PrintFlagsInitial
轻量级锁加锁方式: CAS(设置对象头轻量级锁标志)-> 执行代码块 -> CAS(重置对象头轻量级锁标志) CAS相对于monitorenter和monitorexit的代价更小
轻量级锁不是为了替换重量级锁的而是为了减少使用重量级锁,当轻量级锁失败后,会升级为重量级锁。轻量锁的目标是减少os的介入,减少系统调用(导致内核用户来回切换)。重量锁会让得不到锁的线程阻塞,回涉及到内核用户态的切换,很耗费时间。
轻量锁会先让线程自旋一下等待锁,往往使用于多个线程串行访问同步块,或者竞争没那么大的时候。
一个线程能同时访问一个加锁的方法吗?好像不行吧(偏向锁的出现是因为:同一个线程多次串行访问一个同步块,每次采用重锁又要每次都要尝试CAS将Monitor里面的ower(指的是拥有Monitor锁的线程)从null改为自己,是否成功,而且就算成功了,也需要通过底层os的实现,其实这里我是不太懂的。),所以根本不会出现轻量锁重入的情况,同一个线程只是串行访问一个同步块,轻量锁每次访问完就解锁,怎么会重入呢?一些博客简直在胡扯。
否则自旋获取锁一段事件后还是失败,则膨胀为重量级锁。
使用CAS将对象头替换为当前线程的栈帧中存储锁记录空间的MarkWord。若果成功,则表示没有竞争发生,解锁成功。若失败,表示当前锁存在竞争,锁可能已经升级为重量锁,则采用重量级解锁(见Monitor)。
大佬太强了%%%%%:Java精通并发-通过openjdk源码分析ObjectMonitor底层实现 好想去找男朋友啊啊啊 不要男朋友了 我看不懂看不懂看不懂!:关于偏向锁,安全点,JIT的一些暗坑.