JVM synchronized 详细探究

首先,同步在JVM中是通过monitor进入和monitor退出来实现的。

1、synchronized 方法
  对于synchronized方法,同步作为方法调用和返回的一部分被隐式执行,并不是使用monitorenter和monitorexit来实现方法同步。而是通过运行时常量池中方法对应的ACC_SYNCHRONIZED标志来实现同步。

1.1 synchronized 方法怎么被识别?
  在方法被调用时,调用指令会对方法的ACC_SYNCHRONIZED标志进行检查。

1.2 ACC_SYNCHRONIZED 标志来源于哪里?
  任何方法,包括实例初始化方法或者是类、接口初始化方法,都包含以下结构

method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}

  ACC_SYNCHRONIZED标志正是来自于存在于运行时常量池中该方法对应method_info结构的access_flags字段。

1.3 那这里有没有违背JVM中同步是通过monitor的进入和退出实现呢?
  答案是没有

我们接着往下看
  方法调用指令在检查到method_info对应的ACC_SYNCHRONIZED标志被设置时,执行线程便进入monitor(lock),然后调用该方法,在该方法退出时(无论是正常退出还是非正常退出)关闭monitor(unlock)。执行线程拥有monitor期间,其他线程无法进入该monitor。以此便实现了方法同步。

2、synchronized 代码块
  JVM提供monitorenter和monitorexit指令对synchronized代码块结构进行支持,但要实现同步,还需要针对JVM的编译器的协作来完成。
  这种对代码块的锁定,表现在对monitor上的一enter一exit(即一进一出)。
  由于不是所有提交给JVM的代码都需要执行结构化锁定,所以结构化锁定交由具体的JVM实现来完成。
  这里虚拟机规范提及两个结构化锁的规则(但并不要求虚拟机一定要实现):
  1、不管方法调用是成功还是失败,线程进入monitor和退出monitor的次数必须相等。
  2、绝不会出现在方法调用期间,线程退出monitor的次数超过该线程进入该monitor的次数。

java8虚拟机规范里边关于同步的描述就以上这些。
下面我们就jls8中的部分内容来讨论同步机制。

3 我们先看看java8语言说明中对monitor的描述

monitor描述:每个java对象都关联着一个monitor。线程可对这个monitor进行lock和unlock(加锁和解锁动作)。一次只能有一个线程在该monitor上持有锁。任何其它尝试对该monitor进行加锁的线程都将阻塞,直到线程可以在该monitor上持有锁。
加锁过程
3.1 synchronized代码块
代码执行到synchronized语义------>计算对象的引用------>执行lock动作尝试对该对象的monitor加锁------>

  • 1 加锁动作成功完成------>执行synchronized语义中的body(即被保护代码块)------>代码段执行成功或不成功------>对该对象的monitor的unlock(解锁动作)自动完成
  • 2 加锁动作未成功完成------>阻塞

3.2 synchronized 方法
方法调用指令------>自动执行加锁动作------>
1 实例方法------>对方法被调用的实例对象的monitor加锁
2 类方法------>对方法被定义的类的Class对象的monitor加锁

------>

  • 加锁动作成功完成------>方法体执行------>无论方法体执行成功与否,对该monitor的解锁动作自动执行
  • 加锁动作未成功完成------>阻塞

你可能感兴趣的:(Java,锁)