synchronized底层实现原理及锁优化

一、简述

1️⃣synchronized的作用

①原子性:synchronized 保证语句块内操作是原子的。

②可见性:synchronized 保证可见性(通过“在执行unlock之前,必须先把此变量同步回主内存”实现)。

③有序性:synchronized 保证有序性(通过“一个变量在同一时刻只允许一条线程对其进行lock操作”)。

2️⃣synchronized的使用

①修饰实例方法,对当前实例对象加锁。

②修饰静态方法,多当前类的Class对象加锁。

③修饰代码块,对 synchronized 括号内的对象加锁。

二、实现原理

JVM的同步(synchronized)基于进入和退出管程(Monitor)对象实现, 无论是显式同步(有明确的 monitorenter 和 monitorexit 指令,即同步代码块)还是隐式同步都是如此。

1️⃣方法级的同步

在 Java 语言中,同步用的最多的地方可能是被 synchronized 修饰的同步方法。方法级的同步是隐式,即无需通过字节码指令来控制的,它实现在方法调用和返回操作之中。JVM 可以从方法常量池中的方法表结构(method_info Structure)中的 ACC_SYNCHRONIZED 访问标志区分一个方法是否同步方法。当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先持有 monitor (虚拟机规范中用的是管程一词),然后再执行方法,最后在方法完成(无论是正常完成还是非正常完成)时释放 monitor。

2️⃣代码块的同步

代码块的同步是利用 monitorenter 和 monitorexit 这两个字节码指令。synchronized是JVM实现的一种互斥同步访问方式,底层是基于每个对象的监视器(monitor)来实现的。被 synchronized 修饰的代码,被编译器编译后在前后加上了一组字节指令。

①在代码开始加入了 monitorenter,在代码后面加入了 monitorexit,这两个字节码指令配合完成了 synchronized 关键字修饰代码的互斥访问。

②在虚拟机执行到 monitorenter 指令的时候,会请求获取对象的 monitor 锁,基于 monitor 锁又衍生出一个锁计数器的概念。

③当执行 monitorenter 时,若对象未被锁定时,或者当前线程已经拥有了此对象的 monitor 锁,则锁计数器+1,该线程获取该对象锁。

④当执行 monitorexit 时,锁计数器-1,当计数器为0时,此对象锁就被释放了。那么其它阻塞的线程则可以请求获取该 monitor 锁。

⑤如果获取monitor对象失败,该线程则会进入阻塞状态,直到其他线程释放锁。

这里要注意:

①synchronized 是可重入的,所以不会自己把自己锁死

②synchronized 锁一旦被一个线程持有,其他试图获取该锁的线程将被阻塞。

关于ACC_SYNCHRONIZED 、monitorenter、monitorexit指令,可以看一下下面的反编译代码:

publicclassSynchronizedDemo{publicsynchronizedvoidf(){//这个是同步方法System.out.println("Hello world");}publicvoidg(){synchronized(this){//这个是同步代码块System.out.println("Hello world");}}publicstaticvoidmain(String[]args){}}

使用javap -verbose SynchronizedDemo反编译后得到:

synchronized底层实现原理及锁优化_第1张图片

同步方法,反编译后得到ACC_SYNCHRONIZED标志

synchronized底层实现原理及锁优化_第2张图片

你可能感兴趣的:(synchronized底层实现原理及锁优化)