根据openjdk源码得知,本文只讨论与锁相关的Mark Word部分,Java对象头和锁的关系如下:
mark word存储的是对象的线程id,年代信息,锁标记位,对象状态等
klass存储的是我们类的元数据的指针
|---------------------------------------------------------------------------------------------------------------------------------------------------|
| Object Header (128 bits) |
|---------------------------------------------------------------------------------------------------------------------------------------------------|
| Mark Word (64 bits) | Klass Word (64 bits) | 默认开启指针压缩
|--------------------------------------------------------------------------------------------------------------------------------------------------|
|unused:25|identity_hashcode:31(56) | unused:1 | age:4 | biased_lock:1 | lock:2 | OOP to metadata object | 无锁 01 (biased_lock标记是否是偏向锁)
|-----------------------------------------------------------------------------------------------|----------|---------------------------------------|
| thread:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | lock:2 | OOP to metadata object | 偏向锁 01(biased_lock标记是否是偏向锁)
|-----------------------------------------------------------------------------------------------|----------|--------------------------------------|
| ptr_to_lock_record:62 | lock:2 | OOP to metadata object | 轻量锁 00
|-----------------------------------------------------------------------------------------------|----------|--------------------------------------|
| ptr_to_heavyweight_monitor:62 | lock:2 | OOP to metadata object | 重量锁 10
|-----------------------------------------------------------------------------------------------|----------|---------------------------------------|
| | lock:2 | OOP to metadata object | GC
|-------------------------------------------------------------------------------------------------------------------------------------------------|
通过jdk提供的JOL来看下对象头信息,可以通过maven引入
org.openjdk.jol
jol-core
0.9
没有线程竞争资源,此时的锁为偏向锁,那么既然没有线程竞争资源,为什么要加锁呢,是因为此时虽然没有线程竞争,不保证以后没有线程竞争,为了多线程情况考虑的
偏向锁模拟代码(已经关闭了偏向锁延迟,参数:-XX:BiasedLockingStartupDelay=0)
public static void main(String[] args) throws InterruptedException {
User user = new User();
out.println("before lock");
out.println(ClassLayout.parseInstance(user).toPrintable());
synchronized (user) {
out.println("ing lock");
out.println(ClassLayout.parseInstance(user).toPrintable());
}
}
注:如果代码中调用了对象的hashcode,那么该对象则无法被偏向,会直接升级为轻量级锁,因为此时偏向锁存储要存储线程id,又要存储hashcode,互相矛盾,而轻量级锁的线程id和hashcode等信息是存在当前线程栈中的叫LockRecord的区域中,对象头中只存储指向这个区域的指针(简单理解)。重量级锁是把这些信息保存到monitor对象中,对象头中保存这个对象的地址。
偏向锁退出同步代码块依然是偏向锁
二、轻量级锁
多个线程交替执行,不会抢占资源,这种情况下,锁为轻量级锁且退出同步代码块之后,对象为无锁状态
public static void main(String[] args) throws InterruptedException {
User user = new User();
Thread t1 = new Thread(() -> {
synchronized (user) {
out.println("t1 lock");
out.println(ClassLayout.parseInstance(user).toPrintable());
}
});
t1.start();
t1.join();
Thread t2 = new Thread(() -> {
synchronized (user) {
out.println("t2 lock");
out.println(ClassLayout.parseInstance(user).toPrintable());
}
});
t2.start();
Thread.sleep(1000);
out.println("after lock");
out.println(ClassLayout.parseInstance(user).toPrintable());
}
执行结果
t1 lock
thread.lock.User object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 20 f4 dc 1a (00100000 11110100 11011100 00011010) (450688032)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 20 (01000011 11000001 00000000 00100000) (536920387)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
t2 lock
thread.lock.User object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 20 f4 dc 1a (00100000 11110100 11011100 00011010) (450688032)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 20 (01000011 11000001 00000000 00100000) (536920387)
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
thread.lock.User 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 20 (01000011 11000001 00000000 00100000) (536920387)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total