内容简介:之前的文章中介绍了对其反编译:查看上面反编译的结果,我们可以看到反编译里面是存在
之前的文章中介绍了 JAVA 中一些并发锁使用方法以及里面的介绍。同时之后还介绍了字节码的操作码,让大家先了解下里面的指令,我这里也是从表面中去讲解下锁底层操作码的实现。
锁对象程序:
package com.montos.detail;
public class SynchronizedDemo {
public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo();
demo.demo();
}
public void demo() {
synchronized (this) {
System.out.println("this is demo");
}
}
}
复制代码
对其反编译:
public class com.montos.detail.SynchronizedDemo {
public com.montos.detail.SynchronizedDemo();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class com/montos/detail/SynchronizedDemo
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4 // Method demo:()V
12: return
public void demo();
Code:
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #6 // String this is demo
9: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: aload_1
13: monitorexit
14: goto 22
17: astore_2
18: aload_1
19: monitorexit
20: aload_2
21: athrow
22: return
Exception table:
from to target type
4 14 17 any
17 20 17 any
}
复制代码
查看上面反编译的结果,我们可以看到反编译里面是存在 monitorenter 以及 monitorexit 的操作码,这两个操作码的作用就是:
-
monitorenter:每个对象都是一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
- 如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者;
- 如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1;
- 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权;
-
monitorexit:执行monitorexit的线程必须是objectref所对应的monitor的所有者。指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。
从而达到线程之间的串行执行,同时我可以看到里面有两次 monitorexit 操作码:第1次为同步正常退出释放锁;第2次为发生异步退出释放锁;这上面锁住的就是this。
锁方法程序:
public class SynchronizedDemo {
public synchronized void method() {
System.out.println("this is demo");
}
}
复制代码
反编译:
public com.montos.detail.SynchronizedDemo();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public synchronized void method();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String this is demo
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 5: 0
line 6: 8
复制代码
通过上面反编译,我们发现没有之前的两个操作码了,多出来的是有标识 ACC_SYNCHRONIZED ,这里其实也是通过上面两个操作码完成的。这个方法也只是比普通的方法在常量池中多了 ACC_SYNCHRONIZED 字段。
当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。
上面的两种操作本质上没有区别,只是方法的同步是一种隐式方式操作的,两个指令的执行是JVM通过调用操作系统的互斥原语mutex来实现,被阻塞的线程会被挂起、等待重新调度,会导致“用户态和内核态”两个态之间来回切换,对性能有较大影响。
锁定关键点:
对象在内存中布局主要有:对象头,实例数据以及对齐填充。
Java
Synchronized 用的锁就是存在 Java 对象头里的,那么什么是 Java 对象头呢?Hotspot虚拟机的对象头主要包括两部分数据: Mark Word (标记字段)、 Class Pointer (类型指针)。其中 Class Pointer 是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例, Mark Word 用于存储对象自身的运行时数据,它是实现轻量级锁和偏向锁的关键。
这里面我们主要注意的是 Mark Word 这个存储结构。
每一个 Java 对象创建出来就带了一把看不见的锁,它叫做内部锁或者 Monitor锁 。 Monitor 对象存在于每个 Java 对象的对象头 Mark Word 中(存储的指针的指向), Synchronized 锁便是通过这种方式获取锁的,也是为什么 Java 中任意对象可以作为锁的原因,同时 notify/notifyAll/wait 等方法会使用到 Monitor 锁对象,所以必须在同步代码块中使用。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- ASP.NET Core模块化前后端分离快速开发框架介绍之3、数据访问模块介绍
- 简编漫画介绍WebAssembly
- CGroup 介绍
- CGroup 介绍
- vue初步介绍
- Microbit MicroPython 介绍
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Java程序设计
宋中山 严千钧 等编 / 清华大学出版社 / 2005-8 / 27.00元
本书全面、系统地介绍了Java语言的基本概念、基本语法和编程方法。主要内容包括:Java语言概述、数据类型与运算符、流程控制语句、类与对象、继承与多态、异常处理、工具类和算法、Applet小应用程序、图形用户界面、输入和输出、Java多线程以及Java高级编程。每章后面附有习题,读者可参考使用。 本书内容丰富,结构合理,语言简洁,深入浅出,通俗易懂。基础知识与程序实例相结合,示例典型......一起来看看 《Java程序设计》 这本书的介绍吧!
XML 在线格式化
在线 XML 格式化压缩工具
HSV CMYK 转换工具
HSV CMYK互换工具