【问题标题】:Confusion on Wait ,Notify and Sleep等待、通知和睡眠的混乱
【发布时间】:2014-07-17 03:18:06
【问题描述】:

我有一个简单的程序,我发现它非常混乱。代码sn-p如下:

 class Processor{

      public void produce() Throws InterruptedException{
          synchronized(this){ 

              System.out.println("Producer Running...");
              wait();
               System.out.println("Resumed");

          }
      }


     public void consume() Throws InterruptedException{
          synchronized(this){ 
              Thread.Sleep(2000);
              System.out.println("Consumer Running... Press return key to return");
              scan.nextLine();                
              notify();  
              Thread.sleep(5000);

          }

 }

现在我的问题是,当我们在“produce”方法中调用 wait() 时,执行会立即转移到“consume”方法。 (生产和消费在不同的线程中执行)。但是当 notify();在“消费”方法中被调用,执行不会立即转移。它等待 Thread.sleep(5000) 完成。为什么会这样?

【问题讨论】:

    标签: java multithreading sleep wait notify


    【解决方案1】:

    嗯,原因很简单。

    当线程在某个对象上调用wait() 时,它会进入等待状态并停止执行(从调度中删除)。等待线程时释放它所占用的所有监视器(唤醒后需要重新获得它们)

    当一个线程在某个对象上调用notify() 时,它会唤醒另一个等待它的线程,但它本身并不会进入等待状态,因此它会继续运行。

    在您的生产者线程调用通知之后,它会继续运行并执行五秒钟的睡眠。在睡眠时,线程会保留它所占用的所有监视器(您位于同步(this)块内,因此您有一个“this”对象的监视器)。调度程序无法运行刚刚通知的消费者线程,因为它需要在恢复之前重新查询监视器,并且在您的生产者线程停止睡眠并退出同步块之前它不会被释放

    【讨论】:

      【解决方案2】:

      尽管您似乎遗漏了一些我需要完全准确解释的代码,但即使我的猜测不正确,我也会尽力提供适用的解释。

      wait()notify() 是在互斥对象上调用的方法——在本例中为 this

      wait() 导致当前执行的线程暂停并放弃该互斥锁(我认为这只是调用wait() 的互斥锁,可能是所有这些。不确定),之后另一个线程可以获取互斥锁并开始执行。这就是为什么在执行 wait() 时您会观察到控制权的立即转移。

      当在互斥锁上调用notify() 时,等待该互斥锁的线程将被唤醒并尝试获取锁。但是,在锁可用之前它不能这样做——在这种情况下,直到锁 (this) 被调用 notify() 的线程(消费者线程)释放。仅当消费者线程退出 synchronized 块后,互斥锁才会被释放,这是在代码中的 Thread.sleep(5000); 调用之后。 sleep() 不会释放当前线程已获取的任何互斥锁,因此第一个线程必须等到第二个线程完成睡眠并退出 synchronized 块。

      这就是为什么wait() 立即转移控制权,而notify()(在这种情况下)让当前正在执行的线程在之前等待的线程可以继续执行之前完成其方法。

      【讨论】:

        【解决方案3】:

        假设您使用来自不同线程的相同对象调用这两种方法。

        如果您不想等待 5000 毫秒,请使用 wait(5000) 而不是 Thread.sleep(5000)。

        notify 方法,取一个(随机)先前等待的线程,即等待获取正在运行/当前线程之前占用的(对象的)锁,并将其标记为在当前线程释放后立即恢复锁。

        在您的这种情况下,它将释放锁,很快 Thread.sleep(5000) 完成并离开同步块。

        请注意,如果您使用不同的对象调用生产或消费,事情将完全不同。我强烈建议阅读this 文章。

        希望对您有所帮助!正如下面的好答案!

        【讨论】:

          【解决方案4】:

          原因是 Thread.sleep(5000L) 在等待时不会释放对象监视器上的锁,这与 wait(5000L) 相反。这在 Thread.sleep() 的 Javadoc 中指定:

          ...线程不会失去任何监视器的所有权。

          Object.wait() 的 javadoc 指定:

          ...这个方法使当前线程(称为T)放置自己 在这个对象的等待集合中,然后放弃任何和所有 对此对象的同步声明...

          【讨论】:

            猜你喜欢
            • 2013-05-29
            • 1970-01-01
            • 2012-10-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多