synchronized的实现原理

我们用一个文本文档写一个java类:

public class TestSynchronized{
public void f(){
synchronized(this){
System.out.println("ffffffff invoke");
}}}

  打开cmd,执行javac [.java文件的路径]编译该.java文件,会生成TestSynchronized.class,然后执行javap -c [.class文件的路径]反编译该.class文件,-c表示会打印出方法的字节码指令,效果如下图所示: 
   
  
   
  看见了什么,我标注红色箭头的地方,有两条字节码指令,monitorenter和monitorexit,JVM规范中对这两条字节码的描述如下: 
   
  monitorenter

Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:
• If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.
• If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.
• If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership.

  monitorexit

The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.
The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.

  这两段话就解释了synchronized的实现原理: 
   
  monitorenter: 
  每个对象有一个monitor,即监视器,当且仅当monitor被占用时,这个monitor就被锁住了。线程执行monitorenter指令是为了尝试获取该monitor的所有权,过程为: 
  1) 如果一个monitor的进入数为0,那么该线程直接进入monitor,并且将monitor进入数置为1,该线程成为该monitor的所有者; 
  2) 如果该进程是已经占用该monitor,则直接进入,并且monitor进入数加1; 
  3)如果该进程未占有该monitor,即monitor被其他线程所占有,那么该线程会被阻塞,直到该monitor的进入数变为0,此时该线程会再次尝试获取该monitor。 
   
  monitorexit: 
  执行monitorexit指令的线程必须是已经拥有该monitor的线程,执行monitorexit指令后,该monitor的进入数减1,直到该monitor的进入数减为0,此时该线程不再是该monitor的所有者,其他被阻塞进入该monitor的线程可以尝试获取该monitor的所有权。 
   
  这就是synchronized的实现原理。其实,wait/notify/notifyAll也是基于monitor对象实现的,这也是为什么只有在同步块中才能使用wait/notify/notifyAll方法。 
   
  上面介绍的是synchronized代码块,如果用synchronized修饰方法,会是怎样呢?我们用javap -verbose命令反编译下面的程序,其中-verbose表示输出堆栈大小、各方法的locals及args数,以及class文件的编译版本:

public class TestSynchronized{
public synchronized void f(){
System.out.println("ffffffff invoke");
}}

  核心输出如下: 
   
  
   
  我们发现在方法体内部没有monitorenter和monitorexit指令,但是注意我箭头表示的地方,有一个ACC_SYNCHRONIZED标志,JVM就是通过该标志来判断是否需要实现同步的,具体过程为:当线程执行该方法时,会先检查该方法是否标志了ACC_SYNCHRONIZED,如果标志了,线程需要先获取monitor,获取成功后才能调用方法,方法执行完后再释放monitor,在该线程调用方法期间,其他线程无法获取同一个monitor对象。其实本质上和synchronized块相同,只是同步方法是用一种隐式的方式来实现,而不是显式地通过字节码指令。
 

没有更多推荐了,返回首页

私密
私密原因:
请选择设置私密原因
  • 广告
  • 抄袭
  • 版权
  • 政治
  • 色情
  • 无意义
  • 其他
其他原因:
120
出错啦
系统繁忙,请稍后再试

关闭