您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

30分钟彻底弄懂 synchronized 锁升级过程

时间:2022-07-25 17:16:27  来源:  作者:程序猿阿嘴

JAVA的并发编程领域中,我们进行会使用到锁这个东西,例如在多线程环境下为了预防某些线程安全问题,这里面可能会产生一些预想不到的问题,所以下边我整理了一系列关于JDK中锁的问题,帮助大家更加深入地了解它们。

synchronized真的是重量级锁嘛?

这个问题相信大部分人在面试的时候都有遇到过,答案是否定的。这个要看JDK的版本来进行判断。如果JDK的版本在1.5之前使用synchronized锁的原理大概如下:

  1. 给需要加锁的资源前后分别加入一条“monitorenter”和“monitorexit”指令。
  2. 当线程需要进入这个代码临界区的时候,先去参与“抢锁”(本质是获取monitor的权限)
  3. 抢锁如果失败,就会被阻塞,此时控制权只能交给操作系统,也就会从 user mode 切换到 kernel mode, 由操作系统来负责线程间的调度和线程的状态变更, 需要频繁地在这两个模式下切换(上下文转换)。

可以看出,老模式的条件下去获取锁的开销是比较大的,所以后来JDK的作者才会在JDK中设计了Lock接口,采用CAS的方式来实现锁,从而提升性能。

但是当竞争非常激烈的时候,采用CAS的方式有可能会一直获取不到锁的话,不管进行再多的CAS也是在浪费CPU,这种状态下的性能损耗会比synchronized还要高。所以这类情况下,不如直接升级加锁的方式,让操作系统介入。

正因为这样,所以后边才会有了锁升级的说法。

synchronized的锁升级

偏向锁

在synchronized进行升级的过程中,第一步会升级为偏向锁。所谓偏向锁,它的本质就是让锁来记住请求的线程。

在大多数场景下,其实都是单线程访问锁的情况偏多,JDK的作者在重构synchronized的时候,给对象头设计了一个bit位,专门用于记录锁的信息,具体我们可以通过下边这个实际案例来认识下:

public static void mAIn(String[] args) throws InterruptedException {
    Object o = new Object();
    System.out.println("还没有进入到同步块");
    System.out.println("markword:" + ClassLayout.parseInstance(o).toPrintable());
    //默认JVM启动会有一个预热阶段,所以默认不会开启偏向锁
    Thread.sleep(5000);
    Object b = new Object();
    System.out.println("还没有进入到同步块");
    System.out.println("markword:" + ClassLayout.parseInstance(b).toPrintable());
    synchronized (o){
        System.out.println("进入到了同步块");
        System.out.println("markword:" + ClassLayout.parseInstance(o).toPrintable());
    }
}

注意要引入一些第三方的依赖,辅助我们查看对象头的信息:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    //这个版本号的不同,查看的内容格式也不同
    <version>0.16</version>
</dependency>

控制台输出的结果如下:

还没有进入到同步块
# WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
markword:java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4        (object header: class)    0xf80001e5
 12   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


还没有进入到同步块
markword:java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000005 (biasable; age: 0)
  8   4        (object header: class)    0xf80001e5
 12   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


进入到了同步块
markword:java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x00007000050ee988 (thin lock: 0x00007000050ee988)
  8   4        (object header: class)    0xf80001e5
 12   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

这个案例中,如果你仔细观察控制台的内容,可以发现,当JVM刚启动的时候,对象头部的锁标志位是无锁状态。但是过了一整子(大概4秒之后),就会变成一个biasable的状态。如果需要调整这个延迟的时间,可以通过参数
-XX:BiasedLockingStartupDelay=0 来控制。

这里我解释下biasable的含义:

biasable是JVM帮我们设置的状态,在这种状态下,一旦有线程访问锁,就会直接CAS修改对象头中的线程id。如果成功,则直接升级为偏向锁。否则就会进入到锁的下一个状态--轻量级锁。

ps:JVM因为在启动预热的阶段中,会有很多步骤使用到synchronized,所以在刚启动的前4秒中,不会直接将synchronized锁的标记升级为biasable状态。这是为了减少一些不必要的性能损耗。

轻量级锁

当锁被一个线程访问的时候,它会变成偏向锁的状态,那么当新的线程再次访问该锁的时候,锁会有什么变化吗?

这里我整理了一张锁的变化流程图,如下所示:

30分钟彻底弄懂 synchronized 锁升级过程

 

为了验证这个过程,我们可以通过下边这个案例来实践下:

public static void main(String[] args) throws InterruptedException {
    // 睡眠 5s
    Thread.sleep(5000);
    Object o = new Object();
    System.out.println("未进入同步块,MarkWord 为:");
    System.out.println(ClassLayout.parseInstance(o).toPrintable());
    synchronized (o){
        System.out.println(("进入同步块,MarkWord 为:"));
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }
    Thread t2 = new Thread(() -> {
        synchronized (o) {
            System.out.println("新线程获取锁,MarkWord为:");
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
        }
    });
    t2.start();
    t2.join();
    System.out.println("主线程再次查看锁对象,MarkWord为:");
    System.out.println(ClassLayout.parseInstance(o).toPrintable());
    synchronized (o){
        System.out.println(("主线程再次进入同步块,MarkWord 为:"));
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }
    
    synchronized (b) {
        System.out.println(("主线程再次进入同步块,并且调用hashcode方法,MarkWord 为:"));
        b.hashCode();
        System.out.println(ClassLayout.parseInstance(b).toPrintable());
    }
}

然后我们来观察下执行的结果:

/Library/Java/JavaVirtualmachines/jdk1.8.0_181.jdk/Contents/Home/bin/java -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:63267,suspend=y,server=n -javaagent:/Users/linhao/Library/Caches/IntelliJIdea2019.3/captureAgent/debugger-agent.jar -Dfile.encoding=UTF-8 -classpath "/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/DNSns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/lib/tools.jar:/Users/linhao/IdeaProjects/my-Github/concurrence-programming-lession/target/classes:/Users/linhao/.m2/repository/org/junit/jupiter/junit-jupiter/5.9.0-RC1/junit-jupiter-5.9.0-RC1.jar:/Users/linhao/.m2/repository/org/junit/jupiter/junit-jupiter-api/5.5.2/junit-jupiter-api-5.5.2.jar:/Users/linhao/.m2/repository/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar:/Users/linhao/.m2/repository/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar:/Users/linhao/.m2/repository/org/junit/platform/junit-platform-commons/1.5.2/junit-platform-commons-1.5.2.jar:/Users/linhao/.m2/repository/org/junit/jupiter/junit-jupiter-params/5.5.2/junit-jupiter-params-5.5.2.jar:/Users/linhao/.m2/repository/org/junit/jupiter/junit-jupiter-engine/5.5.2/junit-jupiter-engine-5.5.2.jar:/Users/linhao/.m2/repository/org/junit/platform/junit-platform-engine/1.5.2/junit-platform-engine-1.5.2.jar:/Users/linhao/.m2/repository/org/openjdk/jol/jol-core/0.16/jol-core-0.16.jar:/Users/linhao/.m2/repository/org/Apache/dubbo/dubbo/2.7.8/dubbo-2.7.8.jar:/Users/linhao/.m2/repository/org/springframework/spring-context/5.2.5.RELEASE/spring-context-5.2.5.RELEASE.jar:/Users/linhao/.m2/repository/org/springframework/spring-aop/5.2.5.RELEASE/spring-aop-5.2.5.RELEASE.jar:/Users/linhao/.m2/repository/org/springframework/spring-beans/5.2.5.RELEASE/spring-beans-5.2.5.RELEASE.jar:/Users/linhao/.m2/repository/org/springframework/spring-expression/5.2.5.RELEASE/spring-expression-5.2.5.RELEASE.jar:/Users/linhao/.m2/repository/com/alibaba/spring/spring-context-support/1.0.8/spring-context-support-1.0.8.jar:/Users/linhao/.m2/repository/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA.jar:/Users/linhao/.m2/repository/io.NETty/netty-all/4.1.48.Final/netty-all-4.1.48.Final.jar:/Users/linhao/.m2/repository/com/google/code/gson/gson/2.8.6/gson-2.8.6.jar:/Users/linhao/.m2/repository/org/yaml/snakeyaml/1.25/snakeyaml-1.25.jar:/Users/linhao/.m2/repository/com/alibaba/fastjson/1.2.70/fastjson-1.2.70.jar:/Users/linhao/.m2/repository/org/apache/zookeeper/zookeeper/3.5.3-beta/zookeeper-3.5.3-beta.jar:/Users/linhao/.m2/repository/commons-cli/commons-cli/1.2/commons-cli-1.2.jar:/Users/linhao/.m2/repository/io/netty/netty/3.10.5.Final/netty-3.10.5.Final.jar:/Users/linhao/.m2/repository/org/apache/dubbo/dubbo-spring-boot-starter/2.7.8/dubbo-spring-boot-starter-2.7.8.jar:/Users/linhao/.m2/repository/org/apache/dubbo/dubbo-spring-boot-autoconfigure/2.7.8/dubbo-spring-boot-autoconfigure-2.7.8.jar:/Users/linhao/.m2/repository/org/apache/dubbo/dubbo-spring-boot-autoconfigure-compatible/2.7.8/dubbo-spring-boot-autoconfigure-compatible-2.7.8.jar:/Users/linhao/.m2/repository/org/springframework/boot/spring-boot-starter/2.2.6.RELEASE/spring-boot-starter-2.2.6.RELEASE.jar:/Users/linhao/.m2/repository/org/springframework/boot/spring-boot/2.2.6.RELEASE/spring-boot-2.2.6.RELEASE.jar:/Users/linhao/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.2.6.RELEASE/spring-boot-autoconfigure-2.2.6.RELEASE.jar:/Users/linhao/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.2.6.RELEASE/spring-boot-starter-logging-2.2.6.RELEASE.jar:/Users/linhao/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar:/Users/linhao/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar:/Users/linhao/.m2/repository/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar:/Users/linhao/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.12.1/log4j-to-slf4j-2.12.1.jar:/Users/linhao/.m2/repository/org/apache/logging/log4j/log4j-api/2.12.1/log4j-api-2.12.1.jar:/Users/linhao/.m2/repository/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30.jar:/Users/linhao/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar:/Users/linhao/.m2/repository/org/springframework/spring-core/5.2.5.RELEASE/spring-core-5.2.5.RELEASE.jar:/Users/linhao/.m2/repository/org/springframework/spring-jcl/5.2.5.RELEASE/spring-jcl-5.2.5.RELEASE.jar:/Users/linhao/.m2/repository/org/springframework/boot/spring-boot-starter-web/2.2.6.RELEASE/spring-boot-starter-web-2.2.6.RELEASE.jar:/Users/linhao/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.2.6.RELEASE/spring-boot-starter-json-2.2.6.RELEASE.jar:/Users/linhao/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.10.3/jackson-databind-2.10.3.jar:/Users/linhao/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.10.3/jackson-annotations-2.10.3.jar:/Users/linhao/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.10.3/jackson-core-2.10.3.jar:/Users/linhao/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.10.3/jackson-datatype-jdk8-2.10.3.jar:/Users/linhao/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.10.3/jackson-datatype-jsr310-2.10.3.jar:/Users/linhao/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.10.3/jackson-module-parameter-names-2.10.3.jar:/Users/linhao/.m2/repository/org/springframework/boot/spring-boot-starter-validation/2.2.6.RELEASE/spring-boot-starter-validation-2.2.6.RELEASE.jar:/Users/linhao/.m2/repository/jakarta/validation/jakarta.validation-api/2.0.2/jakarta.validation-api-2.0.2.jar:/Users/linhao/.m2/repository/org/hibernate/validator/hibernate-validator/6.0.18.Final/hibernate-validator-6.0.18.Final.jar:/Users/linhao/.m2/repository/org/jboss/logging/jboss-logging/3.4.1.Final/jboss-logging-3.4.1.Final.jar:/Users/linhao/.m2/repository/com/fasterxml/classmate/1.5.1/classmate-1.5.1.jar:/Users/linhao/.m2/repository/org/springframework/spring-web/5.2.5.RELEASE/spring-web-5.2.5.RELEASE.jar:/Users/linhao/.m2/repository/org/springframework/spring-webmvc/5.2.5.RELEASE/spring-webmvc-5.2.5.RELEASE.jar:/Users/linhao/.m2/repository/org/springframework/boot/spring-boot-starter-Tomcat/2.2.6.RELEASE/spring-boot-starter-tomcat-2.2.6.RELEASE.jar:/Users/linhao/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.33/tomcat-embed-core-9.0.33.jar:/Users/linhao/.m2/repository/org/apache/tomcat/embed/tomcat-embed-el/9.0.33/tomcat-embed-el-9.0.33.jar:/Users/linhao/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.33/tomcat-embed-websocket-9.0.33.jar:/Users/linhao/.m2/repository/org/apache/curator/curator-framework/2.12.0/curator-framework-2.12.0.jar:/Users/linhao/.m2/repository/org/apache/curator/curator-client/2.12.0/curator-client-2.12.0.jar:/Users/linhao/.m2/repository/com/google/guava/guava/16.0.1/guava-16.0.1.jar:/Users/linhao/.m2/repository/org/apache/curator/curator-recipes/2.12.0/curator-recipes-2.12.0.jar:/Users/linhao/.m2/repository/com/alibaba/transmittable-thread-local/2.12.2/transmittable-thread-local-2.12.2.jar:/Users/linhao/.m2/repository/org/apache/rocketmq/rocketmq-client/4.8.0/rocketmq-client-4.8.0.jar:/Users/linhao/.m2/repository/org/apache/rocketmq/rocketmq-common/4.8.0/rocketmq-common-4.8.0.jar:/Users/linhao/.m2/repository/org/apache/rocketmq/rocketmq-remoting/4.8.0/rocketmq-remoting-4.8.0.jar:/Users/linhao/.m2/repository/org/apache/rocketmq/rocketmq-logging/4.8.0/rocketmq-logging-4.8.0.jar:/Users/linhao/.m2/repository/io/netty/netty-tcnative-boringssl-static/2.0.30.Final/netty-tcnative-boringssl-static-2.0.30.Final.jar:/Users/linhao/.m2/repository/commons-validator/commons-validator/1.6/commons-validator-1.6.jar:/Users/linhao/.m2/repository/commons-beanutils/commons-beanutils/1.9.2/commons-beanutils-1.9.2.jar:/Users/linhao/.m2/repository/commons-digester/commons-digester/1.8.1/commons-digester-1.8.1.jar:/Users/linhao/.m2/repository/commons-logging/commons-logging/1.2/commons-logging-1.2.jar:/Users/linhao/.m2/repository/commons-collections/commons-collections/3.2.2/commons-collections-3.2.2.jar:/Users/linhao/.m2/repository/org/apache/commons/commons-lang3/3.9/commons-lang3-3.9.jar:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar" 并发编程03.查看内存布局信息.MarkWordDemo_4
Connected to the target VM, address: '127.0.0.1:63267', transport: 'socket'
# WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
未进入同步块,MarkWord 为:java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4        (object header: class)    0xf80001e5
 12   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


未进入同步块,MarkWord 为:java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000005 (biasable; age: 0)
  8   4        (object header: class)    0xf80001e5
 12   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


进入同步块,MarkWord 为:java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x00007fe8a5009805 (biased: 0x0000001ffa294026; epoch: 0; age: 0)
  8   4        (object header: class)    0xf80001e5
 12   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


新线程获取锁,MarkWord为:
java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x000070000ba03908 (thin lock: 0x000070000ba03908)
  8   4        (object header: class)    0xf80001e5
 12   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


主线程再次查看锁对象,MarkWord为:
java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4        (object header: class)    0xf80001e5
 12   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


主线程再次进入同步块,MarkWord 为:
java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000700009f87980 (thin lock: 0x0000700009f87980)
  8   4        (object header: class)    0xf80001e5
 12   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


主线程再次进入同步块,并且调用hashcode方法,MarkWord 为:
java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x00007fe8a51391ea (fat lock: 0x00007fe8a51391ea)
  8   4        (object header: class)    0xf80001e5
 12   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


Disconnected from the target VM, address: '127.0.0.1:63267', transport: 'socket'


Process finished with exit code 0

通过在控制台中的打印内容我们可以发现,锁的状态一共经历了以下几个变化步骤:

  • biasable状态

在这个状态下,锁是一个带偏向的状态,此时如果有线程请求的话。不过如果是刚启动JVM的状态的话,对象头部会是non-biasable状态,只有等jvm预热了一段时间(大约是4秒),才会留转变为biasable状态。

  • biased状态

当第一个请求获取到锁的时候,锁的状态会变成偏向锁状态,也就是biased。如果在处于偏向锁状态的时候,还有新的线程参与锁的抢夺,那么就会发生锁的升级,进入到轻量级锁状态阶段。

  • thin lock状态

可以看到,当一个锁已经经历过偏向锁状态之后,后去如果再有其他线程访问它,它就会升级为轻量级锁的状态,也就是thin lock状态。

  • fat lock状态

当我们在同步代码块中调用hashcode方法的时候,会发现,锁的对象头部会多出一个叫做fat lock的状态,这就意味着,此时该锁已经升级为了重量级锁的状态了。

重量级锁

当一把锁已经处于轻量级锁的状态时,如果此时又有多个线程来尝试获取锁,那么锁就会被多个线程以自旋的方式来请求访问,当访问的次数达到一定上限之后,synchronized就会自动升级为重量级锁的状态了。

当升级为重量级锁的情况下,锁对象的mark word中的指针不再指向线程栈中的lock record,而是指向堆中与锁对象关联的monitor对象。当多个线程同时访问同步代码时,这些线程会先尝试获取当前锁对象对应的monitor的所有权:

  • 获取成功,判断当前线程是不是重入,如果是重入那么recursions+1
  • 获取失败,当前线程会被阻塞,等待其他线程解锁后被唤醒,再次竞争锁对象

在重量级锁的情况下,加解锁的过程涉及到操作系统的Mutex Lock进行互斥操作,线程间的调度和线程的状态变更过程需要在用户态和核心态之间进行切换,会导致消耗大量的cpu资源,导致性能降低。

有哪几种方式可以使一把锁升级为重量级状态?

  • 调用wait方法
  • 在同步代码块中调用对象的hashcode方法

最后我绘制了一张锁升级的流程图和大家分享下:

30分钟彻底弄懂 synchronized 锁升级过程

 

小结

其实JVM已经对synchronized进行了优化。可以直接用,至于锁的力度如何,JVM底层已经做好了我们直接用就行。不过作为一名工程师,了解这些底层原理还是可以增加我们自身内部的功力的。

原文链接:
https://mp.weixin.qq.com/s/LsV9HKl8AW4vL4rz8iigxw



Tags:synchronized   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
30分钟彻底弄懂 synchronized 锁升级过程
在Java的并发编程领域中,我们进行会使用到锁这个东西,例如在多线程环境下为了预防某些线程安全问题,这里面可能会产生一些预想不到的问题,所以下边我整理了一系列关于JDK中锁的...【详细内容】
2022-07-25  Search: synchronized  点击:(384)  评论:(0)  加入收藏
▌简易百科推荐
Meta如何将缓存一致性提高到99.99999999%
介绍缓存是一种强大的技术,广泛应用于计算机系统的各个方面,从硬件缓存到操作系统、网络浏览器,尤其是后端开发。对于Meta这样的公司来说,缓存尤为重要,因为它有助于减少延迟、扩...【详细内容】
2024-04-15    dbaplus社群  Tags:Meta   点击:(3)  评论:(0)  加入收藏
SELECT COUNT(*) 会造成全表扫描?回去等通知吧
前言SELECT COUNT(*)会不会导致全表扫描引起慢查询呢?SELECT COUNT(*) FROM SomeTable网上有一种说法,针对无 where_clause 的 COUNT(*),MySQL 是有优化的,优化器会选择成本最小...【详细内容】
2024-04-11  dbaplus社群    Tags:SELECT   点击:(3)  评论:(0)  加入收藏
10年架构师感悟:从问题出发,而非技术
这些感悟并非来自于具体的技术实现,而是关于我在架构设计和实施过程中所体会到的一些软性经验和领悟。我希望通过这些分享,能够激发大家对于架构设计和技术实践的思考,帮助大家...【详细内容】
2024-04-11  dbaplus社群    Tags:架构师   点击:(2)  评论:(0)  加入收藏
Netflix 是如何管理 2.38 亿会员的
作者 | Surabhi Diwan译者 | 明知山策划 | TinaNetflix 高级软件工程师 Surabhi Diwan 在 2023 年旧金山 QCon 大会上发表了题为管理 Netflix 的 2.38 亿会员 的演讲。她在...【详细内容】
2024-04-08    InfoQ  Tags:Netflix   点击:(5)  评论:(0)  加入收藏
即将过时的 5 种软件开发技能!
作者 | Eran Yahav编译 | 言征出品 | 51CTO技术栈(微信号:blog51cto) 时至今日,AI编码工具已经进化到足够强大了吗?这未必好回答,但从2023 年 Stack Overflow 上的调查数据来看,44%...【详细内容】
2024-04-03    51CTO  Tags:软件开发   点击:(9)  评论:(0)  加入收藏
跳转链接代码怎么写?
在网页开发中,跳转链接是一项常见的功能。然而,对于非技术人员来说,编写跳转链接代码可能会显得有些困难。不用担心!我们可以借助外链平台来简化操作,即使没有编程经验,也能轻松实...【详细内容】
2024-03-27  蓝色天纪    Tags:跳转链接   点击:(16)  评论:(0)  加入收藏
中台亡了,问题到底出在哪里?
曾几何时,中台一度被当做“变革灵药”,嫁接在“前台作战单元”和“后台资源部门”之间,实现企业各业务线的“打通”和全域业务能力集成,提高开发和服务效率。但在中台如火如荼之...【详细内容】
2024-03-27  dbaplus社群    Tags:中台   点击:(14)  评论:(0)  加入收藏
员工写了个比删库更可怕的Bug!
想必大家都听说过删库跑路吧,我之前一直把它当一个段子来看。可万万没想到,就在昨天,我们公司的某位员工,竟然写了一个比删库更可怕的 Bug!给大家分享一下(不是公开处刑),希望朋友们...【详细内容】
2024-03-26  dbaplus社群    Tags:Bug   点击:(9)  评论:(0)  加入收藏
我们一起聊聊什么是正向代理和反向代理
从字面意思上看,代理就是代替处理的意思,一个对象有能力代替另一个对象处理某一件事。代理,这个词在我们的日常生活中也不陌生,比如在购物、旅游等场景中,我们经常会委托别人代替...【详细内容】
2024-03-26  萤火架构  微信公众号  Tags:正向代理   点击:(15)  评论:(0)  加入收藏
看一遍就理解:IO模型详解
前言大家好,我是程序员田螺。今天我们一起来学习IO模型。在本文开始前呢,先问问大家几个问题哈~什么是IO呢?什么是阻塞非阻塞IO?什么是同步异步IO?什么是IO多路复用?select/epoll...【详细内容】
2024-03-26  捡田螺的小男孩  微信公众号  Tags:IO模型   点击:(10)  评论:(0)  加入收藏
站内最新
站内热门
站内头条