synchronized锁升级过程

531 阅读2分钟

synchronized

先看看synchronized在字节码层面是如何加锁的,代码如下

public class SyncByteCode {
    public void sync(){
        synchronized (this){
            System.out.println("synchronized");
        }
    }

    public static void main(String[] args) {
        new SyncByteCode().sync();
    }
}

我们把它反编译成字节码

javap -c target/classes/com/hoolai/sync/SyncByteCode.class 
Compiled from "SyncByteCode.java"
public class com.hoolai.sync.SyncByteCode {
  public com.hoolai.sync.SyncByteCode();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void sync();
    Code:
       0: aload_0
       1: dup
       2: astore_1
       //monitorenter负责加锁
       3: monitorenter
       4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       7: ldc           #3                  // String synchronized
       9: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      12: aload_1
      //monitorenter负责解锁
      13: monitorexit
      14: goto          22
      17: astore_2
      18: aload_1
      //防止异常解锁失败
      19: monitorexit
      20: aload_2
      21: athrow
      22: return
    Exception table:
       from    to  target type
           4    14    17   any
          17    20    17   any

  public static void main(java.lang.String[]);
    Code:
       0: new           #5                  // class com/hoolai/sync/SyncByteCode
       3: dup
       4: invokespecial #6                  // Method "<init>":()V
       7: invokevirtual #7                  // Method sync:()V
      10: return
}

从字节码层面可以看出每个monitorenter有与它匹配的moniterexit

对象头

synchronized锁的是对象头,对象头包含了markword,对象类型,(如果是数组的话)数组长度 先看看32位机器的MarkWord对象的数据结构

偏向锁

  • 锁原理 代码如下,直接运行程序
public class ObjectHead {
    public static void main(String[] args) throws Exception{
        ObjectHead head = new ObjectHead();
        System.out.println(ClassLayout.parseInstance(head).toPrintable());
    }
}

对象处于无锁状态

加上-XX:BiasedLockingStartUpDelay=0命令

处于偏向锁状态 偏向锁在系统启动几秒后才会开启,可以通过-XX:BiasedLockingStartupDelay=0来让程序启动时就变成偏向锁,如果系统存在锁竞争,则建议通过-XX:-UseBiasedLocking=false来关闭偏向锁。因为偏向锁的撤销会带来消耗

使用synchronized加锁后 代码如下

public class ObjectHead {
    private String name = "ObjectHead";
    public static void main(String[] args) throws Exception{
        ObjectHead head = new ObjectHead();
        synchronized (head) {
            System.out.println(ClassLayout.parseInstance(head).toPrintable());
        }
    }
}

MarkWord状态

MarkWord指向线程的ID

  • 锁升级过程
  • 锁升级代码
  • 锁升级对象头

轻量级锁

在执行同步代码的时候,jvm会把MarkWord复制到线程栈的锁记录中(displaced MarkWord)。然后利用cas将MarkWord指向栈中的锁记录,释放的时候利用cas将替换displaced MarkWord 替换为markword,轻量级锁采用自旋操作,会占用cpu资源

  • 锁对象头

  • 锁升级

重量级锁