Java对象头分析synchronized加锁过程

根据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());
        }
    }

执行结果:Java对象头分析synchronized加锁过程_第1张图片

注:如果代码中调用了对象的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


 

你可能感兴趣的:(java线程基础,java,多线程,并发编程,jvm)