美文网首页程序员
JVM synchronized锁实现原理,看完还不懂算我输!!

JVM synchronized锁实现原理,看完还不懂算我输!!

作者: yuwei149 | 来源:发表于2020-06-08 23:08 被阅读0次

    # JVM synchronized锁实现原理,看完还不懂算我输!!!

    > 本文将用代码带大家一探究竟,看看JVM究竟是如何实现线程同步以及锁的升级过程, jdk版本为`HotSpot 64bit 1.8.0_91`, Talking is cheap, show me the code!!!

    ## 一、synchronized关键字的实现

    > 我们将一段java代码通过`javap -c -s`命令反编译一下看看

    ```java

    package com.ywzlp.demo;

    /**

    * Created by yuwei on 2020/6/6

    */

    public class TestMonitor {

        public static void main(String[] args) {

            Object lock = new Object();

            synchronized (lock) {

                System.out.println("hello lock");

            }

        }

    }

    /*

        首先执行javac TestMonitor.java编译

        再执行 javap -c -s TestMonitor.class进行反编译

        结果如下:

        public class com.ywzlp.demo.TestMonitor {

      public com.ywzlp.demo.TestMonitor();

        descriptor: ()V

        Code:

          0: aload_0

          1: invokespecial #1                  // Method java/lang/Object."<init>":()V

          4: return

      public static void main(java.lang.String[]);

        descriptor: ([Ljava/lang/String;)V

        Code:

          0: new          #2                  // class java/lang/Object

          3: dup

          4: invokespecial #1                  // Method java/lang/Object."<init>":()V

          7: astore_1

          8: aload_1

          9: dup

          10: astore_2

          11: monitorenter

          12: getstatic    #3                  // Field java/lang/System.out:Ljava/io/PrintStream;

          15: ldc          #4                  // String hello lock

          17: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V

          20: aload_2

          21: monitorexit

          22: goto          30

          25: astore_3

          26: aload_2

          27: monitorexit

          28: aload_3

          29: athrow

          30: return

        Exception table:

          from    to  target type

              12    22    25  any

              25    28    25  any

    }

    */

    ```

    我们可以看到,synchronized被编译后会生成两条指令,`monitorenter` 和 `monitorexit`,下面带大家看看这两条指令具体会做些什么操作

    ## 二、java对象在内存中的存储布局

    > 在讲锁实现之前,我们需要了解java对象的布局,可以使用open-jdk的jol包,全称Java object layout,可以看到java对象在内存中的布局,maven地址:

    ```xml

    <dependency>

        <groupId>org.openjdk.jol</groupId>

        <artifactId>jol-core</artifactId>

        <version>0.10</version>

    </dependency>

    ```

    #### 我们看看下面程序运行结果

    ```java

    package com.ywzlp.demo;

    import org.openjdk.jol.info.ClassLayout;

    /**

    * Created by yuwei on 2020/6/6

    */

    public class TestLock {

        private int num;

        private String str;

        public static void main(String[] args) {

            TestLock testLock = new TestLock();

            System.out.println(ClassLayout.parseInstance(testLock).toPrintable());

        }

    }

    /**运行结果

    com.ywzlp.demo.TestLock 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)                          05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)

        12    4                int TestLock.num                              0

        16    4  java.lang.String TestLock.str                              null

        20    4                    (loss due to the next object alignment)

    Instance size: 24 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    *

    */

    ```

    #### 对象布局包含下面几个部分

    1. markword(包含锁、GC、hashCode信息),占用8个字节

    2. klass pointer (类型指针),开启指针压缩并且JVM内存小于32G时占用4个字节,未开启指针压缩或者JVM内存大于32G占用8个字节

    3. 数组长度(数组对象才有),占用4个字节

    4. 成员变量数据(基本类型存的数据,对象类型存的引用指针)

    5. padding(对齐,如果对象大小不是8的整数倍将补齐)

    ## 三、synchronized锁升级过程

    > 锁信息存放在对象的markword中,下面是markword的结构

    ```text

    |--------------------------------------------------------------------------------|--------------------|

    |                                  Mark Word (64 bits)                          |      锁状态   |

    |--------------------------------------------------------------------------------|--------------------|

    | unused:25 | identity_hashcode:31 | cms_free:1 | age:4 | biased_lock:1 | lock:2 |        无锁   |

    |--------------------------------------------------------------------------------|--------------------|

    | thread:54 |      epoch:2        | cms_free:1 | age:4 | biased_lock:1 | lock:2 |      偏向锁   |

    |--------------------------------------------------------------------------------|--------------------|

    |                        ptr_to_lock_record                            | lock:2 | 轻量级锁   |

    |--------------------------------------------------------------------------------|--------------------|

    |                    ptr_to_heavyweight_monitor                        | lock:2 | 重量级锁   |

    |--------------------------------------------------------------------------------|--------------------|

    ```

    #### 对象一共有五个状态

    锁状态 | markword标志位

    :---:|:---:

    无锁 | 001

    偏向锁 | 101

    轻量级锁(自旋锁)| 00

    重量级锁 | 10

    标记GC | 11

    代码演示标示位变化

    ```java

    package com.ywzlp.demo;

    import org.openjdk.jol.info.ClassLayout;

    import java.util.ArrayList;

    import java.util.List;

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

    import java.util.concurrent.Future;

    /**

    * -XX:BiasedLockingStartupDelay=0

    * Created by yuwei on 2020/6/6

    */

    public class TestLock {

        private int num;

        private String str;

        public static void main(String[] args) throws InterruptedException {

            TestLock testLock = new TestLock();

            System.out.println("---------------无锁boundary-----------------");

            System.out.println(ClassLayout.parseClass(TestLock.class).toPrintable(testLock));

            System.out.println("---------------无锁boundary-----------------");

            System.out.println("---------------偏向锁boundary-----------------");

            testLock.add(500);

            System.out.println("---------------偏向锁boundary-----------------");

            System.out.println("---------------轻量级锁boundary-----------------");

            Thread t1 = new Thread(() -> testLock.add(500));

            t1.start();

            t1.join();

            System.out.println("---------------轻量级锁boundary-----------------");

            System.out.println("---------------重量级锁boundary-----------------");

            ExecutorService executorService = Executors.newFixedThreadPool(2);

            List<Future<?>> futureList = new ArrayList<>(2);

            for (int i = 0; i < 2; i++) {

                futureList.add(executorService.submit(() -> testLock.add(500)));

            }

            futureList.forEach(future -> {

                try {

                    future.get();

                } catch (Exception ignored) {}

            });

            executorService.shutdown();

            futureList.clear();

            System.out.println("---------------重量级锁boundary-----------------");

        }

        public synchronized void add(int timeToSleep) {

            if (timeToSleep > 0) {

                try {

                    Thread.sleep(timeToSleep);

                } catch (InterruptedException ignored) {

                }

            }

            num++;

            System.out.println(ClassLayout.parseClass(TestLock.class).toPrintable(this));

        }

    }

    /*

        运行结果:

        ---------------无锁boundary-----------------

    com.ywzlp.demo.TestLock object internals:

    OFFSET  SIZE              TYPE DESCRIPTION                              VALUE

          0    4                    (object header)                          05 00 00 00 (00000101 00000000 00000000 00000000) (5)

          4    4                    (object header)                          00 00 00 00 (00000000 00000000 00000000 00000000) (0)

          8    4                    (object header)                          05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)

        12    4                int TestLock.num                              0

        16    4  java.lang.String TestLock.str                              null

        20    4                    (loss due to the next object alignment)

    Instance size: 24 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ---------------无锁boundary-----------------

    ---------------偏向锁boundary-----------------

    com.ywzlp.demo.TestLock object internals:

    OFFSET  SIZE              TYPE DESCRIPTION                              VALUE

          0    4                    (object header)                          05 48 80 25 (00000101 01001000 10000000 00100101) (629164037)

          4    4                    (object header)                          fa 7f 00 00 (11111010 01111111 00000000 00000000) (32762)

          8    4                    (object header)                          05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)

        12    4                int TestLock.num                              1

        16    4  java.lang.String TestLock.str                              null

        20    4                    (loss due to the next object alignment)

    Instance size: 24 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ---------------偏向锁boundary-----------------

    ---------------轻量级锁boundary-----------------

    com.ywzlp.demo.TestLock object internals:

    OFFSET  SIZE              TYPE DESCRIPTION                              VALUE

          0    4                    (object header)                          f0 f7 4b 07 (11110000 11110111 01001011 00000111) (122419184)

          4    4                    (object header)                          00 70 00 00 (00000000 01110000 00000000 00000000) (28672)

          8    4                    (object header)                          05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)

        12    4                int TestLock.num                              2

        16    4  java.lang.String TestLock.str                              null

        20    4                    (loss due to the next object alignment)

    Instance size: 24 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ---------------轻量级锁boundary-----------------

    ---------------重量级锁boundary-----------------

    com.ywzlp.demo.TestLock object internals:

    OFFSET  SIZE              TYPE DESCRIPTION                              VALUE

          0    4                    (object header)                          3a b1 82 26 (00111010 10110001 10000010 00100110) (646099258)

          4    4                    (object header)                          fa 7f 00 00 (11111010 01111111 00000000 00000000) (32762)

          8    4                    (object header)                          05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)

        12    4                int TestLock.num                              3

        16    4  java.lang.String TestLock.str                              null

        20    4                    (loss due to the next object alignment)

    Instance size: 24 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    com.ywzlp.demo.TestLock object internals:

    OFFSET  SIZE              TYPE DESCRIPTION                              VALUE

          0    4                    (object header)                          3a b1 82 26 (00111010 10110001 10000010 00100110) (646099258)

          4    4                    (object header)                          fa 7f 00 00 (11111010 01111111 00000000 00000000) (32762)

          8    4                    (object header)                          05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)

        12    4                int TestLock.num                              4

        16    4  java.lang.String TestLock.str                              null

        20    4                    (loss due to the next object alignment)

    Instance size: 24 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ---------------重量级锁boundary-----------------

    */

    ```

    > 注意在运行时需要加上JVM参数-XX:BiasedLockingStartupDelay=0,这个参数代表启动时就开启偏向锁.JVM默认会延迟几秒钟开启偏向锁,是因为JVM在启动时会有多线程锁竞争,而在偏向锁升级到轻量级锁时会有一系列的复杂过程,所以在明知道会有竞争的情况下干脆不启用偏向锁。

    我们可以看到markword第6-8位的打印如下:

    `101 -> 101 -> 000 -> 010 -> 010`

    为什么没有看到无锁的001状态?

    因为一旦开启了偏向锁后,创建对象默认会是匿名偏向状态,此时markword的threadId为空,001状态大家可以在上一个程序中看到

    #### 转载请注明出处

    相关文章

      网友评论

        本文标题:JVM synchronized锁实现原理,看完还不懂算我输!!

        本文链接:https://www.haomeiwen.com/subject/zoqjtktx.html