synchronize在jdk1.5之前就是一个重量级的锁,是在jvm层面加锁的一种形式,在字节码运行的过程中被翻译成了两个指令,速度很慢。jkd1.6开始对synchronize进行了非常多的优化,使sync有了一个锁升级机制,可以让sync在不同的场景下加不同的锁,大大提升了sync的效率。sync一共有三种锁状态:偏向锁、轻量级锁、重量级锁。下面通过查看class对象头的方式来查看这几个锁状态是怎么表现的。
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;
import static java.lang.System.out;
public class JOLExample1 {
public static void main(String[] args) throws Exception {
A a = new A();
out.println(VM.current().details());
out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
com.wave.test.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
通过查看value可以发现没有加锁的对象的状态是01(无锁状态)。
偏向锁在对象头上有一个bit用来标识偏向状态,所以如果是101就是偏向锁
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;
import static java.lang.System.out;
//没有竞争,理论上是偏向锁
public class JOLExample2 {
static A a;
public static void main(String[] args)throws Exception{
a = new A();
out.println("befre lock");
out.println(ClassLayout.parseInstance(a).toPrintable());
sync();
out.println("after lock");
out.println(ClassLayout.parseInstance(a).toPrintable());
}
public static void sync() throws InterruptedException {
synchronized (a){
System.out.println("我也不知道要打印什么");
}
}
}
神奇的发现加了锁还是01,这里是因为偏向锁会有一个4s的延迟,修改虚拟机参数或者睡眠5s就可以看到101了(XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0)
befre lock
com.wave.test.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
我也不知道要打印什么
after lock
com.wave.test.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
一个对象如果被两个线程加了锁,那就会变成轻量级锁,如果被两个线程竞争了,就会变成重量级锁
//轻量级锁
public class JOLExample3 {
static A a;
public static void main(String[] args)throws Exception{
//Thread.sleep(5000);
a = new A();
out.println("befre lock");
new Thread(()->{
sync();
}).start();
Thread.sleep(10000);//如果减小这个值,让两个线程发生竞争,就会产生重量级锁
out.println("after lock");
//out.println(ClassLayout.parseInstance(a).toPrintable());
sync();
}
public static void sync() {
synchronized (a){
try {
out.println(ClassLayout.parseInstance(a).toPrintable());
}catch (Exception e){
}
}
}
}