Java thread synchronized wait 搭配使用,释放锁的一个问题

2019-07-18 11:49:31 +08:00
 b0644170fc
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 状态后,释放锁。那虚拟机是怎么实现的呢?

2785 次点击
所在节点    Java
12 条回复
shily
2019-07-18 13:18:38 +08:00
wait 不是通过 monitorexit 释放锁的,或者称为它只是让出了时间片,进入等待队列,但没有释放锁。不然,当其他的线程 notify/notifyAll() 后如何获得锁(没有 monitorenter )?
Aruforce
2019-07-18 13:55:07 +08:00
你都 wait(释放对象锁进入等待区阻塞)了,需要其他线程获取 thread 的对象锁来 notify 啊
zwb9412
2019-07-18 14:04:13 +08:00
"从字节码上来看,看不出线程进入 wait 状态后,释放锁。"
5: invokevirtual #2 // Method java/lang/Object.wait:()V
调的 Object.wait()方法。自己类的字节码肯定看不出来了
b0644170fc
2019-07-18 14:04:37 +08:00
@shily https://howtodoinjava.com/java/multi-threading/sleep-vs-wait/ 很多教程或博客都说 wait 会释放锁
b0644170fc
2019-07-18 14:06:47 +08:00
@Aruforce 我觉得“你都 wait(释放对象锁进入等待区阻塞)了” 这句话不对。调用 wait 方法,只是让线程进入 wait 状态,并没有进入 blocked 状态
lihongjie0209
2019-07-18 14:14:45 +08:00
对于 monitor 的任何操作都必须持有 monitor 所关联的锁, 这是一个操作系统的问题, 不是 java 线程的问题. 可以去看看操作系统中关于锁的实现部分.
b0644170fc
2019-07-18 14:51:51 +08:00
@lihongjie0209 monitor 那块的内容其实是 jvm 的内容
lihongjie0209
2019-07-18 15:02:21 +08:00
@lihongjie0209 操作系统的, 只是 jvm 带了一个实现而已
Aruforce
2019-07-18 15:35:53 +08:00
@b0644170fc block 是死锁 ,wait 是阻塞,有问题? monitorenter 和 monitorexit 这里 monitor 就是对象的锁啊(JVM 对操作系统锁的抽象或者替代)...
而 monitor 的锁不在字节码里...在 JVM 下一层也就是操作系统
shily
2019-07-18 15:36:41 +08:00
@b0644170fc #4 我的(#1 )前半段是对的,但后半段错误了。
可以这样理解,monitorenter/monitorexit 不是获取和释放锁的唯一方式,还有一些方式并没有通过 JVM 提现出来,比如 wait() 过程会有一个锁的释放、获取过程,但它不是通过 monitorenter/monitorexit 指令实现的,所以没有在字节码上提现出来。但实际上确实有一个锁的释放过程,等其它的线程 notify/notifyAll 时,它也确实有一个锁的获取过程。
hfc
2019-07-19 10:07:20 +08:00
以下是我的个人理解:
wait()方法是 native 方法,虚拟机在执行到 invokevirtual 时是会去找具体实现的(比如在多态调用时,调用的是具体的子类实现),那么在这里 wait()的实际执行应该是交给底层 C 库了,所以锁释放和获取在 jvm 字节码上应该是体现不出来的。
cmonkey
2019-07-19 11:03:37 +08:00
没有人叫你起来

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/584078

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX