Java 在不同方法中对同一对象进行同步块

Java synchronize block on same object in different methods

我正在尝试理解 java 中同步块的概念。 根据我阅读的文件,我了解到如果我们获得 一个锁(使用实例变量的同步块)然后我们 无法在 class 中的同一对象上获取同步锁。但 当我实际尝试使用以下代码片段时,我发现我的 理解出错了。

即我能够获得锁 (同一实例变量上的同步块)在两个不同的 同时方法。当线程启动时,它将转到 运行 方法并无限期地等待并且不会从同步中出来 堵塞。同时,如果我使用相同的方法调用停止方法 线程它进入同步块并执行通知 陈述。我在 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 的 运行 方法中的同步块)。当我调用停止方法时,它应该无限期地等待,直到 运行 方法从同步块中出来(在这种情况下永远不会发生)。但是当我执行时,调用 stop 方法获取了 'lock' 对象上的锁并通知了导致线程关闭的对象。

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

之后执行

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

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

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

现在,执行将始终停止。

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

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

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

public final void wait() throws InterruptedException

Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0).

The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:

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

This method should only be called by a thread that is the owner of this object's monitor. See the notify method for a description of the ways in which a thread can become the owner of a monitor.

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