2

我试图理解java中同步块的概念。根据我阅读的文档,我了解到如果我们获取锁(使用实例变量的同步块),那么我们无法在该类中的同一对象上获取同步锁。但是当我实际尝试使用以下代码段时,我发现我的理解出错了。

即我能够同时在两种不同的方法中获取锁(同一实例变量上的同步块)。当线程启动时,它将进入 run 方法并无限期地等待,并且不会退出同步块。同时,如果我使用同一个线程调用 stop 方法,它会进入同步块并执行通知语句。我在 Java 文档中进行了搜索,但找不到任何内容。

这是代码片段:

public class MyClass extends Thread
{
    private Object lock = new Object(); 
    public void run()
    {
      synchronized(lock)
      {
          lock.wait()
      }
      //other code
    }
    public void stop()
    {
      synchronized(lock)
      {
          lock.notify()
      }
      //other code
    } 
}

这是我如何管理 MyClass 线程的代码片段:

public class MyClassAdmin 
{
    MyClass _myclass;
    public MyClassAdmin()
    {
        _myclass=new MyClass();
        _myclass.start();
    }
    public void stop()
    {
    _myclass.stop();
    }
    public static void main(String args[])
    {
    MyClassAdmin _myclassAdmin=new MyClassAdmin();
    _myclassAdmin.stop();
    }
}

根据我的理解,当线程启动时,它将获取“锁定”对象的锁定(MyClass 的运行方法中的同步块)。当我调用 stop 方法时,它应该无限期地等待,直到 run 方法从同步块中出来(在这种情况下永远不会发生)。但是当我执行时,调用停止方法获取了“锁”对象上的锁并通知了导致线程关闭的对象。

4

2 回答 2

2

您的两种方法都使用相同的锁。如果您的 MyClass 线程碰巧在主线程可以调用 stop 方法之前开始等待,您的 stop 方法仍然可以继续,因为等待线程释放了锁。一旦线程进入等待方法,它就会在休眠之前释放锁,并且在退出等待方法之前不会重新获取锁。

这是Object#wait 的相关 API 文档,第二段介绍了我上面描述的关于 wait 如何释放锁的内容。注意它说你必须在循环中调用这个方法的部分,否则你有一个顺序依赖的错误,当通知到达主线程时,另一个线程可以开始等待,导致等待线程挂起。

public final void wait() 抛出 InterruptedException

使当前线程等待,直到另一个线程为此对象调用 notify() 方法或 notifyAll() 方法。换句话说,此方法的行为与它只是执行调用 wait(0) 完全相同。

当前线程必须拥有该对象的监视器。线程释放此监视器的所有权并等待,直到另一个线程通过调用 notify 方法或 notifyAll 方法通知在此对象的监视器上等待的线程唤醒。然后线程等待直到它可以重新获得监视器的所有权并恢复执行。

与单参数版本一样,中断和虚假唤醒是可能的,并且应该始终在循环中使用此方法:

 synchronized (obj) {
     while (<condition does not hold>)
         obj.wait();
     ... // Perform action appropriate to condition
 }  

此方法只能由作为该对象监视器所有者的线程调用。有关线程可以成为监视器所有者的方式的描述,请参见 notify 方法。

理解这是一个玩具示例,但是子类化 Thread 和覆盖 Thread 方法是令人困惑的。使用 Runnable 而不是 Thread 的原因之一是没有机会通过错误地覆盖 Thread 方法而导致问题。

于 2016-03-02T14:22:40.813 回答
-2

这是多线程的,它可能永远等待也可能不会。在您的情况下,您很幸运 _myclassAdmin.stop(); 在 MyClass 开始执行并执行 wait() 之后执行;

我在将方法 stop() 名称更改为 stop1() 后运行程序,它一直在等待。

为了获得一致的行为做一件事,在 main 中的两个方法调用之间放置 1 秒睡眠,例如:

MyClassAdmin _myclassAdmin=new MyClassAdmin();
Thread.sleep(1)
_myclassAdmin.stop();

现在,执行将永远停止。

此外,当线程调用 wait() 时,它会释放与其关联的监视器,因此任何其他线程都可以获取该锁并发出 notify() / notifyAll() 以唤醒等待线程。这是期待

于 2016-03-02T12:21:13.200 回答