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资源
- 锁对象头
- 锁升级