public class MyThread extends Thread {
@Override
public void run() {
synchronized (this) {
try {
this.wait();
System.out.println("4353");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
程序的运行结果确实是 MyThread 线程一直处于 wait 状态,通过 jstack 来看也是如此。后面的打印字符串语句没有执行。我的问题在 run 方法的字节码,以下是该方法的字节码
public void run();
Code:
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: aload_0
5: invokevirtual #2 // Method java/lang/Object.wait:()V
8: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
11: ldc #4 // String 4353
13: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
16: goto 24
19: astore_2
20: aload_2
21: invokevirtual #7 // Method java/lang/InterruptedException.printStackTrace:()V
24: aload_1
25: monitorexit
26: goto 34
29: astore_3
30: aload_1
31: monitorexit
32: aload_3
33: athrow
34: return
Exception table:
from to target type
4 16 19 Class java/lang/InterruptedException
4 26 29 any
29 32 29 any
从 run 方法的字节码来看,monitorenter 之后,跟着两个 monitorexit 指令。一个是正常退出释放锁,一个是异常退出释放锁。从字节码上来看,看不出线程进入 wait 状态后,释放锁。那虚拟机是怎么实现的呢?
1
shily 2019-07-18 13:18:38 +08:00
wait 不是通过 monitorexit 释放锁的,或者称为它只是让出了时间片,进入等待队列,但没有释放锁。不然,当其他的线程 notify/notifyAll() 后如何获得锁(没有 monitorenter )?
|
2
Aruforce 2019-07-18 13:55:07 +08:00
你都 wait(释放对象锁进入等待区阻塞)了,需要其他线程获取 thread 的对象锁来 notify 啊
|
3
zwb9412 2019-07-18 14:04:13 +08:00
"从字节码上来看,看不出线程进入 wait 状态后,释放锁。"
5: invokevirtual #2 // Method java/lang/Object.wait:()V 调的 Object.wait()方法。自己类的字节码肯定看不出来了 |
4
b0644170fc OP @shily https://howtodoinjava.com/java/multi-threading/sleep-vs-wait/ 很多教程或博客都说 wait 会释放锁
|
5
b0644170fc OP @Aruforce 我觉得“你都 wait(释放对象锁进入等待区阻塞)了” 这句话不对。调用 wait 方法,只是让线程进入 wait 状态,并没有进入 blocked 状态
|
6
lihongjie0209 2019-07-18 14:14:45 +08:00
对于 monitor 的任何操作都必须持有 monitor 所关联的锁, 这是一个操作系统的问题, 不是 java 线程的问题. 可以去看看操作系统中关于锁的实现部分.
|
7
b0644170fc OP @lihongjie0209 monitor 那块的内容其实是 jvm 的内容
|
8
lihongjie0209 2019-07-18 15:02:21 +08:00
@lihongjie0209 操作系统的, 只是 jvm 带了一个实现而已
|
9
Aruforce 2019-07-18 15:35:53 +08:00
@b0644170fc block 是死锁 ,wait 是阻塞,有问题? monitorenter 和 monitorexit 这里 monitor 就是对象的锁啊(JVM 对操作系统锁的抽象或者替代)...
而 monitor 的锁不在字节码里...在 JVM 下一层也就是操作系统 |
10
shily 2019-07-18 15:36:41 +08:00
@b0644170fc #4 我的(#1 )前半段是对的,但后半段错误了。
可以这样理解,monitorenter/monitorexit 不是获取和释放锁的唯一方式,还有一些方式并没有通过 JVM 提现出来,比如 wait() 过程会有一个锁的释放、获取过程,但它不是通过 monitorenter/monitorexit 指令实现的,所以没有在字节码上提现出来。但实际上确实有一个锁的释放过程,等其它的线程 notify/notifyAll 时,它也确实有一个锁的获取过程。 |
11
hfc 2019-07-19 10:07:20 +08:00
以下是我的个人理解:
wait()方法是 native 方法,虚拟机在执行到 invokevirtual 时是会去找具体实现的(比如在多态调用时,调用的是具体的子类实现),那么在这里 wait()的实际执行应该是交给底层 C 库了,所以锁释放和获取在 jvm 字节码上应该是体现不出来的。 |
12
cmonkey 2019-07-19 11:03:37 +08:00
没有人叫你起来
|