5

Java并发编程专题系列之深入分析synchronized(基础篇)

 3 years ago
source link: https://my.oschina.net/liboware/blog/5054980
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

synchronized同步关键字简介

synchronized是属于JVM层面的一个关键字,底层是通过一个monitor对象(管程对象)来完成,由于wait()/notify()等方法也依赖于monitor对象,所以只有在同步的块或者方法中才能调用wait/notify等方法

synchronized关键字

方法字节码展示

synchronized同步代码块底层实现

  • synchronized同步语句块的实现使用monitorentermonitorexit 指令
    • monitorenter:指令指向同步代码块的开始位置
    • monitorexit:指令则指明同步代码块的结束位置

synchronized获取锁

  • 当执行monitorenter指令时,当前线程将试图获取 objectref(即对象锁) 所对应的 monitor的持有权,当 objectref的monitor 的进入计数器**为0,那线程可以成功取得 monitor,并将计数器值设置为 1,取锁成功。

  • 如果当前线程已经拥有 objectref 的 monitor 的持有权,那它可以重入这个 monitor (关于重入性稍后会分析),重入时计数器的值也会加 1。

  • 倘若其他线程已经拥有objectref 的 monitor 的所有权,那当前线程将被阻塞,直到正在执行线程执行完毕,即monitorexit指令被执行,执行线程将释放monitor(锁)并设置计数器值为0,其他线程将有机会持有 monitor ;**

为了保证在方法异常完成时 monitorenter 和 monitorexit 指令依然可以正确配对执行,编译器会自动产生一个异常处理器,这个异常处理器声明可处理所有的异常,它的目的就是用来执行 monitorexit 指令

从字节码中也可以看出多了一个monitorexit指令,它就是异常结束时被执行的释放monitor的指令;

monitorenter(进入管程对象)

每个对象有一个管程对象(monitor),当monitor被占用时就会处于锁定状态;线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

  1. 如果monitor的进入计数器为0,则该线程进入monitor,然后将进入计数器设置为1,该线程即为monitor的所有者;

  2. 如果线程已经之前占用了该monitor,本次只是重新进入,则将monitor的进入计数器加1;

  3. 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入计数器为0,再重新尝试获取monitor的所有权;

monitorexit(退出管程对象)

  • 执行monitorexit的线程必须是objectref(即对象锁)所对应的monitor的所有者;

  • 指令执行时,monitor的进入计数器减1,如果减1后进入计数器为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权.

同步方法中synchronized底层实现

方法级的同步是隐式,即无需通过字节码指令(monitorenter和monitorexit)来控制的。 它实现在方法调用和返回操作之中。

  • JVM可以从方法常量池中的方法表结构(method_info Structure) 中的ACC_SYNCHRONIZED访问标志区分一个方法是否同步方法。

  • 当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先持有monitor(虚拟机规范中用的是管程一词)。

  • 然后再执行方法,最后在方法完成(无论是正常完成还是非正常完成)时释放monitor。

在方法执行期间,执行线程持有了monitor,其他任何线程都无法再获得同一个monitor。如果一个同步方法执行期间抛出了异常,并且在方法内部无法处理此异常,那这个同步方法所持有的monitor将在异常抛到同步方法之外时自动释放。



About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK