Java中的锁
在 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 权限,可能报错
成功的效果如下: