说明:java
java中的同步(synchronized)是基于进入monitor对象和退出monitor对象来实现的,不管是显式同步仍是隐式同步。
synchronized语句块:线程
1)(使用javap -c 类名)将class文件反编译后能够看到:同步块的入口位置和出口位置(方法结束处和异常处)分别插入了monitorenter字节码指令和monitorexit字节码指令,故同步代码块属于显示同步。 2)线程执行到monitorenter指令时,尝试获取对象的锁。
synchronized方法:code
1)JVM从Class文件中的方法结构(method_info)中的 ACC_SYNCHRONIZED 访问标志区分一个方法是否为同步方法,故同步方法属于隐式同步。 2)当方法调用时,调用指令会检查方法的ACC_SYNCHRONIZED访问标志是否被设置,若是设置了,执行线程将先持有monitor,而后再执行方法,最后在方法完成时释放monitor。 3)在方法执行期间,其它线程没法获取该monitor。 4)若是一个同步方法执行期间抛出了异常,而且在方法内部没法处理此异常,那这个同步方法所持有的monitor将在异常抛到同步方法以外时自动释放。 method_info 结构格式以下(java虚拟机规范中的摘录): method_info { u2 access_flags; // 用于定义当前方法的访问权限和基本属性的标志 u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; } method_info结构中访问标记(access_flags)的取值: 标记名 值 说明 ACC_PUBLIC 0x0001 public,方法能够从包外访问 ACC_PRIVATE 0x0002 private,方法只能本类中访问 ACC_PROTECTED 0x0004 protected,方法在自身和子类能够访问 ACC_STATIC 0x0008 static,静态方法 ACC_FINAL 0x0010 final,方法不能被重写 ACC_SYNCHRONIZED 0x0020 synchronized,方法由monitor同步 ... 获取锁和释放锁的内存原语: 当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被monitor保护的临界区代码必须从主内存中读取共享变量。 当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中 即: 1将本地内存中的数据设置为无效, 2从主内存中将数据复制到本地内存中, 3在本地内存中进行操做, 4操做完成后将本地内存中的数据刷新到主内存中。总体看起来就像是直接在主内存中操做同样。 synchronized的可重入性: 1)当一个线程再次请求本身持有对象锁的临界资源时,这种状况属于重入锁,请求将会成功。 2)因为synchronized是基于monitor实现的,故每次重入,monitor中的计数器仍会加1。