V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
b0644170fc
V2EX  ›  Java

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

  •  
  •   b0644170fc · 2019-07-18 11:49:31 +08:00 · 2876 次点击
    这是一个创建于 1985 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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 状态后,释放锁。那虚拟机是怎么实现的呢?

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