首页 文章

JAVA以不同的方法在同一对象上同步块

提问于
浏览
2

我试图理解java中synchronized块的概念 . 至于我读过的文档,我明白如果我们获取一个锁(使用实例变量的同步块),那么我们就无法在该类中的同一个对象上获取同步锁 . 但是,当我尝试使用以下代码段时,我发现我的理解是错误的 .

即我能够同时在两种不同的方法中获取锁(同一实例变量上的同步块) . 当线程启动时,它将转到run方法并无限期地等待,并且不会从synchronized块中出来 . 同时,如果我使用相同的线程调用stop方法,它将进入synchronized块并执行notify语句 . 我在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();
    }
}

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

2 回答

  • 1

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

    我将方法stop()名称更改为stop1()之后运行程序,它正在等待 .

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

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

    现在,执行将永远停止 .

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

  • -2

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

    这是相关的API doc for Object#wait,第二段涵盖了我上面描述的等待释放锁的方法 . 注意它必须在循环中调用此方法的部分,否则您有一个顺序依赖性错误,当通知到达主线程之前,其他线程可以开始等待时,可能导致等待线程挂起 .

    public final void wait()throws InterruptedException导致当前线程等待,直到另一个线程调用此对象的notify()方法或notifyAll()方法 . 换句话说,此方法的行为就像它只是执行调用wait(0)一样 . 当前线程必须拥有此对象的监视器 . 该线程释放此监视器的所有权并等待,直到另一个线程通过调用notify方法或notifyAll方法通知等待此对象监视器的线程唤醒 . 然后线程等待,直到它可以重新获得监视器的所有权并继续执行 . 与在一个参数版本中一样,中断和虚假唤醒是可能的,并且此方法应始终在循环中使用:synchronized(obj){
    while(<条件不成立>)
    obj.wait();
    ... //执行适合条件的操作
    }
    此方法只应由作为此对象监视器所有者的线程调用 . 有关线程可以成为监视器所有者的方式的说明,请参阅notify方法 .

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

相关问题