Synchronized底层实现原理

synchronized介绍:

synchronized 关键字在多线程环境下作为线程安全的同步锁

synchronized作用:

1.同步代码块(当前对象锁[this] 或 自定义对象锁) 
2.同步静态方法(当前类的Class实例,Class数据存在永久代中,该类全局锁)
3.同步静态方法(当前对象锁)

同步代码块:

//Java代码
public void syncronizedTest(){
    synchronized (this){
        System.out.println("hello world");
    }
}

//代码反汇编 
JVM使用monitorentermonitorexit两个指令实现同步,即JVM为代码块的前后真正生成了两个字节码指令来实现同步功能。
monitorenter/monitorexit:
    每个对象都会与一个monitor(监视器锁)相关联,当某个monitor被拥有后就会被锁住,当线程执行到monitorenter指令时,就会去尝试获得对应的monitor

    1.每个monitor维护一个记录拥有次数的计数器,未被拥有monitor的该计数器为0,当一个线程获得monitor(执行monitorenter)后,该计数器自增变为1
    2.当同一个线程再次获得该monitor的时候,计数器再次自增     
    3.当不同线程想要获得该monitor的时候,就会被阻塞
    4.当同一个线程释放monitor(执行monitorexit指令)时,计数器自减。当计数器减为0时,monitor将被释放,其他线程可获得该monitor    
  

同步方法(静态方法,非静态方法):

//静态方法
public static synchronized void syncronizedStaticTest(){
    System.out.println("hello world");
} 

//非静态方法
public synchronized void syncronizedTest(){
    System.out.println("hello world");
}

//静态方法与非静态方法反汇编

JVM使用acc_synchronized标识来实现,即JVM通过在方法访问标识符(flags)中加入acc_synchronized来实现同步功能

    同步方法是隐式的,一个同步方法会在运行时常量池中的method_info结构体中存放acc_synchronized标识符。      当一个线程访问方法时,会去检查是否存在acc_synchronized标识符,如果存在,则要先获得对应的monitor锁,然后执行方法,当方法执行结束(正常return或抛出异常)都会释放对应的monitor锁。如果此时有其他线程也想要访问这个方法时,会因得不到monitor锁而阻塞。

总结:

同步方法和同步代码块底层都是通过monitor来实现同步的。

同步方法和同步代码块的区别:
    同步方法是通过方法中的access_flags中设置acc_synchronized标志来实现,同步代码块是通过monitorenter和monitorexit来实现。每个对象都与monitor相关联,而monitor可以被线程拥有或释放。