在 Java 中主要2种加锁机制: synchronized 关键字 java.util.concurrent.Lock (ReentrantLock是该接口的一个常用实现)
两者在底层存在一些差别:
synchronized 是关键字,通过一对字节码指令 monitorenter/monitorexit 实现。
java.util.concurrent.Lock 利用 Java 代码和sun.misc.Unsafe 中的本地调用实现的。Unsafe 包不是Java规范的一部分。
使用 synchronized 有以下三种作用范围: 1.在静态方法上加锁 2.在非静态方法上加锁 3.在代码块上加锁,synchronized (lock)
public class MySynchronized { private Object lock = new Object(); public synchronized static void staticMethod() { // } public synchronized void nonStaticMethod() { // } public void normalMethod() { synchronized (lock) { // } } }注意:上述三种情况,锁都是加在对象上面的。
作用范围
锁对象
非静态方法
当前实例对象 => this
静态方法
类对象 => 当前类.class (注意,它是个类对象)
代码块
指定对象 => lock (以上面的代码为例)
接下来讲下 synchronized 锁
我们知道Java 的对象一般存在堆中,其实jvm中一个对象分为两部分:对象头与对象体。前者又分:Mark Word 、Klass Word 、及数组长度,其中数组长度是数组对象才有的。
以64位JVM为例,Mark Word 、Klass Word 都是64 位的。后者是指针,指向方法区中类的元信息。Mark Word 就与锁有关了。
对于Mark word 的数据结构,网上很多(该图来源于网络)
|------------------------------------------------------------------------------|--------------------| | Mark Word (64 bits) | State | |------------------------------------------------------------------------------|--------------------| | unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 | Normal | |------------------------------------------------------------------------------|--------------------| | thread:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | lock:2 | Biased | |------------------------------------------------------------------------------|--------------------| | ptr_to_lock_record:62 | lock:2 | Lightweight Locked | |------------------------------------------------------------------------------|--------------------| | ptr_to_heavyweight_monitor:62 | lock:2 | Heavyweight Locked | |------------------------------------------------------------------------------|--------------------| | | lock:2 | Marked for GC | |------------------------------------------------------------------------------|--------------------|下边来看下这个对象头
先运行代码,(注意:以server 方式)
public class TestVM { private Object lock = new Object(); public static void main(String[] args) throws InterruptedException { TestVM testVM = new TestVM(); for (; ; ) { testVM.normalMethod(); } } public void normalMethod() throws InterruptedException { synchronized (lock) { // Thread.sleep(1000); } } }JPS
JPS是Java自带的查看java进程的命令
-q: 只显示VM 标示,不显示jar,class, main参数等信息。 -m: 输出主函数传入的参数。 -l: 输出应用程序主类完整package名称或jar完整名称。 -v: 列出jvm启动参数。 -V: 输出通过.hotsportrc或-XX:Flags=<filename>指定的jvm参数。
JDB
HSDB
HSDB,即 Hotspot Debugger,jar包在 JAVA_HOME 下。我的路径是
/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/lib/sa-jdi.jar在该目录下用root权限执行命令
sudo java -cp sa-jdi.jar sun.jvm.hotspot.HSDB启动后,尝试连接需要检查的进程
输入jps 查到的进程ID
注意:如果不是root 权限,可能报错
成功的效果如下: