在JVM启动以后,默认有4s的偏向延迟时间,意思就是,在JVM启动的时候,4s中以内创建的对象是默认为不可偏向的。例如static 静态对象。可以用代码来验证一下

朴实无华类A,用于加锁

  1. package com.edu.enior.bf.objheaderedu;
  2. public class A {
  3. }

验证类

  1. package com.edu.enior.bf.objheaderedu;
  2. import org.openjdk.jol.info.ClassLayout;
  3. public class PrintObjInfo {
  4. //静态对象
  5. static A a = new A();
  6. public static void main(String[] args) throws InterruptedException {
  7. Thread t1 = new Thread(() -> {
  8. lockTest();
  9. });
  10. t1.setName("t1");
  11. t1.start();
  12. }
  13. static void lockTest(){
  14. synchronized (a){
  15. //打印线程名称
  16. System.out.println(Thread.currentThread().getName());
  17. //查看对象内存信息
  18. System.out.println(ClassLayout.parseInstance(a).toPrintable());
  19. }
  20. }
  21. }

image.png

可以看到00,00代表的是轻量锁

接下来让主线程休眠6秒

  1. package com.edu.enior.bf.objheaderedu;
  2. import org.openjdk.jol.info.ClassLayout;
  3. public class PrintObjInfo {
  4. public static void main(String[] args) throws InterruptedException {
  5. Thread.sleep(6000);
  6. //在休眠之后创建锁对象
  7. A a = new A();
  8. Thread t1 = new Thread(() -> {
  9. lockTest(a);
  10. });
  11. t1.setName("t1");
  12. t1.start();
  13. }
  14. static void lockTest(A a){
  15. synchronized (a){
  16. //打印线程名称
  17. System.out.println(Thread.currentThread().getName());
  18. //查看对象内存信息
  19. System.out.println(ClassLayout.parseInstance(a).toPrintable());
  20. }
  21. }
  22. }

image.png
此时10为偏向锁。

偏向延迟的原因

因为JVM启动的时候,需要用到很多锁,但是对于偏向锁,有一个偏向撤销操作,偏向撤销是一个非常耗资源的操作,所以JVM的开发者,就直接先把偏向锁设置为JVM启动4s以后。可以通过以下命令来维护JVM偏向锁状态

  1. //关闭偏向延迟
  2. -XX:BiasedLockingStartupDelay=0
  3. //禁用偏向锁
  4. -XX:-UseBiasedLocking
  5. //启用偏向锁
  6. -XX:+UseBiasedLocking

偏向撤销

应用

有些时候,在线上环境,能确定自己加了synchronized关键字的方法是被一个线程调用的才开启偏向锁,一般线上的项目,基本上都是把偏向锁关闭的,原因就是偏向撤销太耗资源。