一篇文章彻底了解synchronized

  一. synchronized锁的定义

   synchronized同步锁一个老生常谈的话题,synchronized本身是一种支持可重入,实现对临界资源的同步互斥访问,且作用粒度是对象,jvm级别互斥的锁。

  目前主流的加锁的方式主要有以下三种:

1、修饰实例方法,锁定当前实例对象

    int i = 0;       public synchronized void increase() {      i++;    }

2、修饰静态方法,锁定当前类对象,非实例对象,不同实例对象依然互斥

/** * @description: TODO * @author: ppx * @date: 2023-07-19 8:42 * @version: 1.0 */public class Test {
    public static void main(String[] args) {        //不同的实例        Cat yellowCat = new Cat();        //不同的实例        Cat redCat = new Cat();        new Thread(()->{            yellowCat.eat();        },"threadYellowCat").start();
        new Thread(()->{            redCat.eat();        },"threadRedCat").start();
    }}class Cat {
    /**     * @description:     * @param:     * @return: void     * @author: ppx     * @date: 2023-07-19 9:39     */    public synchronized static void eat() {        for (int i = 0; i < 5; i ++) {            try {                System.out.println(Thread.currentThread().getName() + ":" + i);                Thread.sleep(100);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }

}

执行结果:

"C:\Program Files\Java\jdk1.8.0_291\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2022.3.2\lib\idea_rt.jar=65160:C:\Program Files\JetBrains\IntelliJ IDEA 2022.3.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_291\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_291\jre\lib\rt.jar;G:\myProc\tiny-code\tiny-admin\target\classes;D:\repo\org\springframework\boot\spring-boot-starter-jdbc\2.5.6\spring-boot-starter-jdbc-2.5.6.jar;D:\repo\org\springframework\boot\spring-boot-starter\2.5.6\spring-boot-starter-2.5.6.jar;D:\repo\org\springframework\boot\spring-boot-starter-logging\2.5.6\spring-boot-starter-logging-2.5.6.jar;D:\repo\ch\qos\logback\logback-classic\1.2.6\logback-classic-1.2.6.jar;D:\repo\ch\qos\logback\logback-core\1.2.6\logback-core-1.2.6.jar;D:\repo\org\apache\logging\log4j\log4j-to-slf4j\2.14.1\log4j-to-slf4j-2.14.1.jar;D:\repo\org\slf4j\jul-to-slf4j\1.7.32\jul-to-slf4j-1.7.32.jar;D:\repo\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\repo\org\yaml\snakeyaml\1.28\snakeyaml-1.28.jar;D:\repo\com\zaxxer\HikariCP\4.0.3\HikariCP-4.0.3.jar;D:\repo\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;D:\repo\org\springframework\spring-jdbc\5.3.12\spring-jdbc-5.3.12.jar;D:\repo\org\springframework\spring-beans\5.3.12\spring-beans-5.3.12.jar;D:\repo\org\springframework\spring-tx\5.3.12\spring-tx-5.3.12.jar;D:\repo\org\springframework\boot\spring-boot-starter-web\2.5.6\spring-boot-starter-web-2.5.6.jar;D:\repo\org\springframework\boot\spring-boot-starter-json\2.5.6\spring-boot-starter-json-2.5.6.jar;D:\repo\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.12.5\jackson-datatype-jdk8-2.12.5.jar;D:\repo\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.12.5\jackson-datatype-jsr310-2.12.5.jar;D:\repo\com\fasterxml\jackson\module\jackson-module-parameter-names\2.12.5\jackson-module-parameter-names-2.12.5.jar;D:\repo\org\springframework\boot\spring-boot-starter-tomcat\2.5.6\spring-boot-starter-tomcat-2.5.6.jar;D:\repo\org\apache\tomcat\embed\tomcat-embed-core\9.0.54\tomcat-embed-core-9.0.54.jar;D:\repo\org\apache\tomcat\embed\tomcat-embed-el\9.0.54\tomcat-embed-el-9.0.54.jar;D:\repo\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.54\tomcat-embed-websocket-9.0.54.jar;D:\repo\org\springframework\spring-web\5.3.12\spring-web-5.3.12.jar;D:\repo\org\springframework\spring-webmvc\5.3.12\spring-webmvc-5.3.12.jar;D:\repo\org\springframework\spring-context\5.3.12\spring-context-5.3.12.jar;D:\repo\org\springframework\spring-expression\5.3.12\spring-expression-5.3.12.jar;D:\repo\org\projectlombok\lombok\1.18.22\lombok-1.18.22.jar;D:\repo\org\springframework\spring-core\5.3.12\spring-core-5.3.12.jar;D:\repo\org\springframework\spring-jcl\5.3.12\spring-jcl-5.3.12.jar;D:\repo\mysql\mysql-connector-java\5.1.46\mysql-connector-java-5.1.46.jar;D:\repo\commons-fileupload\commons-fileupload\1.3.1\commons-fileupload-1.3.1.jar;D:\repo\commons-io\commons-io\2.2\commons-io-2.2.jar;D:\repo\com\oracle\database\jdbc\ojdbc8\21.1.0.0\ojdbc8-21.1.0.0.jar;D:\repo\commons-net\commons-net\3.6\commons-net-3.6.jar;D:\repo\cn\hutool\hutool-all\4.0.0\hutool-all-4.0.0.jar;D:\repo\org\springframework\boot\spring-boot-starter-aop\2.5.6\spring-boot-starter-aop-2.5.6.jar;D:\repo\org\springframework\spring-aop\5.3.12\spring-aop-5.3.12.jar;D:\repo\org\aspectj\aspectjweaver\1.9.7\aspectjweaver-1.9.7.jar;D:\repo\aopalliance\aopalliance\1.0\aopalliance-1.0.jar;D:\repo\org\springframework\boot\spring-boot-devtools\2.5.6\spring-boot-devtools-2.5.6.jar;D:\repo\org\springframework\boot\spring-boot\2.5.6\spring-boot-2.5.6.jar;D:\repo\org\springframework\boot\spring-boot-autoconfigure\2.5.6\spring-boot-autoconfigure-2.5.6.jar;D:\repo\com\alibaba\fastjson\1.2.78\fastjson-1.2.78.jar;D:\repo\io\minio\minio\7.1.4\minio-7.1.4.jar;D:\repo\com\carrotsearch\thirdparty\simple-xml-safe\2.7.1\simple-xml-safe-2.7.1.jar;D:\repo\com\google\guava\guava\25.1-jre\guava-25.1-jre.jar;D:\repo\org\checkerframework\checker-qual\2.0.0\checker-qual-2.0.0.jar;D:\repo\com\google\errorprone\error_prone_annotations\2.1.3\error_prone_annotations-2.1.3.jar;D:\repo\com\google\j2objc\j2objc-annotations\1.1\j2objc-annotations-1.1.jar;D:\repo\org\codehaus\mojo\animal-sniffer-annotations\1.14\animal-sniffer-annotations-1.14.jar;D:\repo\com\squareup\okhttp3\okhttp\3.14.9\okhttp-3.14.9.jar;D:\repo\com\squareup\okio\okio\1.17.2\okio-1.17.2.jar;D:\repo\com\fasterxml\jackson\core\jackson-annotations\2.12.5\jackson-annotations-2.12.5.jar;D:\repo\com\fasterxml\jackson\core\jackson-core\2.12.5\jackson-core-2.12.5.jar;D:\repo\com\fasterxml\jackson\core\jackson-databind\2.12.5\jackson-databind-2.12.5.jar;D:\repo\com\github\spotbugs\spotbugs-annotations\4.0.0\spotbugs-annotations-4.0.0.jar;D:\repo\net\jcip\jcip-annotations\1.0\jcip-annotations-1.0.jar;D:\repo\com\google\code\findbugs\jsr305\3.0.2\jsr305-3.0.2.jar;D:\repo\org\apache\httpcomponents\httpclient\4.2.1\httpclient-4.2.1.jar;D:\repo\org\apache\httpcomponents\httpcore\4.4.14\httpcore-4.4.14.jar;D:\repo\commons-codec\commons-codec\1.15\commons-codec-1.15.jar;D:\repo\io\jsonwebtoken\jjwt\0.9.1\jjwt-0.9.1.jar;D:\repo\com\itextpdf\itextpdf\5.5.11\itextpdf-5.5.11.jar;D:\repo\com\itextpdf\tool\xmlworker\5.5.11\xmlworker-5.5.11.jar;D:\repo\org\apache\poi\poi-ooxml\3.9\poi-ooxml-3.9.jar;D:\repo\org\apache\poi\poi\3.9\poi-3.9.jar;D:\repo\org\apache\poi\poi-ooxml-schemas\3.9\poi-ooxml-schemas-3.9.jar;D:\repo\org\apache\xmlbeans\xmlbeans\2.3.0\xmlbeans-2.3.0.jar;D:\repo\stax\stax-api\1.0.1\stax-api-1.0.1.jar;D:\repo\dom4j\dom4j\1.6.1\dom4j-1.6.1.jar;D:\repo\xml-apis\xml-apis\1.0.b2\xml-apis-1.0.b2.jar;D:\repo\com\huaweicloud\esdk-obs-java-bundle\3.23.3\esdk-obs-java-bundle-3.23.3.jar;D:\repo\org\apache\logging\log4j\log4j-core\2.14.1\log4j-core-2.14.1.jar;D:\repo\org\apache\logging\log4j\log4j-api\2.14.1\log4j-api-2.14.1.jar" com.test.TestthreadYellowCat:0threadYellowCat:1threadYellowCat:2threadYellowCat:3threadYellowCat:4threadRedCat:0threadRedCat:1threadRedCat:2threadRedCat:3threadRedCat:4
Process finished with exit code 0

总结:

虽然定义不同的实例对象 ,threadYellowCat,threadRedCat 两个线程依然串行依次执行,synchronized静态方法锁定当前类而非实例对象。实例对象调用某一个synchronized方法时,其他同步方法需要等待上一个同步方法执行完成释放锁之后才能执行。

3、修饰代码块,锁定指定对象(括号中包含指定的对象)

 //保持线程直接可见 volatile Cat cat = null; public void play() {       if (cat == null) {            //锁定Cat             synchronized (Cat.class) {                cat = new Cat();            }        }
    }

二、synchronized底层原理

    synchronized基于Monitor实现,通过Monitor对象的进出,来实现方法与代码块同步;Monitor依赖底层操作系统的Mutex lock(互斥锁)实现,java1.5之前,属于一个重量级锁,性能偏低。自jdk 1.6 JVM内置锁进行升级优化,性能扶摇而上,实现对锁粗化(Lock Coarsening)、锁消除(Lock Elimination)、轻量级锁(Lightweight Locking)、偏向锁(Biased Locking)、适应性自旋(Adaptive Spinning)技术的支持。

    synchronized核心指令被转换成monitorenter 和 monitorexit 两条指令分别在同步块代码的起始位置与结束位置。

图片

每个同步对象皆有自己的Monitor(监视器),加锁过程如下图所示:

图片

被synchronized 修饰后,都会有一个 monitor 与java对象关联,monitor 本身就是监视器,可以理解成为一把锁,当线程想要执行一段被synchronized 修饰的方法或者代码块时,该线程需要先获取到 synchronized 修饰的对象对应的 monitor。

三、Monitor监视器锁

每个 Java 对象底层都可以关联一个 Monitor 对象,当一个Monitor被持有后,它将处于锁定状态。Synchronized在JVM里的实现都是基于进入和退出Monitor对象来实现方法同步和代码块同步,最终通过成对的MonitorEnter和MonitorExit指令来实现。

monitorenter

   monitorenter 表示去获得一个对象监视器。monitorexit 表示释放 monitor 监视器的所有权,使得其他被阻塞的线程可以再次尝试获得这个监视器。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权。过程如下:

1、若monitor的进入数为0,线程进入monitor,将进入数设置为1,当前线程成为monitor的所有者;

2、若线程已拥有monitor的所有权,该线程可以重入monitor,进入monitor的进入数加1,synchronized成为可重入锁的原因所在。

 3、若monitor已被其他线程占用,则该线程进入阻塞状态,直到monitor的进入数为0,可以再次重新尝试获取monitor的所有权;

monitorexit:执行monitorexit的线程必须是当前对象持有monitor的所有权的线程。

1、指令执行monitorexit时,则monitor的进入数减1,如果减1后进入数为0时,该线程退出monitor,不再是这个monitor的所有者。被这个monitor阻塞的线程可以尝试重新获取这个 monitor 的所有权。

   2、 monitorexit释放锁,指令出现了两次,第1次为同步正常退出释放锁;第2次为发生异步退出释放锁;

    monitorexit插入在方法末尾处和异常处,JVM保证每个monitorenter必须有对应的monitorexit。

    synchronized底层是基于monitor的机制实现,java 对象wait/notify方法底层同样依赖于monitor对象,这就是为什么在调用wait/notify方法必须用Synchronized修饰,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。

   感谢您的观看,下一篇介绍锁膨胀升级过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值