简体   繁体   English

IllegalMonitorStateException在其run方法内部的线程上调用wait()(无同步块)

[英]IllegalMonitorStateException calling wait() on a thread inside its run method (with no synchronized block)

I order to ensure that the thread which is reading data from a socket is stopped when the socket closes (due to the fact socket.isClosed() doesn't work as expected) I wrote a "heartbeat" to check if the socket is still open. 我要确保在套接字关闭时停止从套接字读取数据的线程(由于socket.isClosed()不能按预期工作),我写了一个“心跳”来检查套接字是否仍然存在打开。 The method startHeartbeat() is called just before the BufferedReader starts reading the socket, and it only starts reading when isClosed() is false. 刚好在BufferedReader开始读取套接字之前调用startHeartbeat()方法,并且仅在isClosed()为false时才开始读取方法。

There are some synchronized methods in play here, but unlike other similar questions, the wait() call is not within one of the methods. 这里有一些同步方法在起作用,但是与其他类似问题不同, wait()调用不在这些方法之一内。 Here is the essential code: 这是基本代码:

synchronized boolean isClosed()
{
    return closed;
}

synchronized void setClosed(boolean b)
{
    closed = b;
}

//We need to make sure that the socket is still online, to ensure the reading stops when the connection closes.
void startHeartbeat()
{
    Thread heartbeat = new Thread()
    {
        public void run()
        {
            while (true)
            {
                try
                {
                    post(THUMP_THUMP);
                    setClosed(false);
                }
                catch (IOException e)
                {
                    setClosed(true);
                }
                finally
                {
                    try
                    {
                        this.wait(PULSE); //Exception here!
                    }
                    catch (InterruptedException e) {}
                }
            }
        }
    };
    heartbeat.setDaemon(true);
    heartbeat.start();
}

THUMP_THUMP is just a constant string that is sent out (the post() method just writes the object out on a BufferedWriter ) and PULSE is the time between beats. THUMP_THUMP只是发出的常量字符串( post()方法只是将对象写到BufferedWriter ),而PULSE是拍子之间的时间。

I'm not sure why there is an IllegalMonitorStateException here, after reading various threads (I understand why they have the exception) and reading the API for the exception. 在读取各种线程(我理解它们为何具有异常)并读取该异常的API之后,我不确定为什么这里有一个IllegalMonitorStateException Can anybody tell me what I am doing wrong here? 有人可以告诉我我在做什么错吗?

Can anybody tell me what I am doing wrong here? 有人可以告诉我我在做什么错吗?

Yes - you're not synchronizing on the object you're waiting on. 是的-您没有在等待的对象上进行同步。 The API documentation is pretty clear: API文档非常清楚:

Throws: 抛出:
IllegalMonitorStateException - if the current thread is not the owner of the object's monitor. IllegalMonitorStateException如果当前线程不是对象监视器的所有者。

It's important to understand that "owner of the objects monitor" basically means "is in a synchronized block using that object". 重要的是要理解“对象所有者的监视者”基本上意味着“处于使用该对象的同步块中”。 A synchronized block acquires the relevant object's monitor at the start, and releases it at the end. 同步块在开始时获取相关对象的监视器,并在结束时释放它。 From the Java tutorial on synchronization : Java同步教程

Synchronization is built around an internal entity known as the intrinsic lock or monitor lock. 同步是围绕称为内部锁定或监视器锁定的内部实体构建的。 (The API specification often refers to this entity simply as a "monitor.") Intrinsic locks play a role in both aspects of synchronization: enforcing exclusive access to an object's state and establishing happens-before relationships that are essential to visibility. (API规范通常将此实体简称为“监视器”。)内在锁在同步的两个方面都起作用:强制对对象状态的独占访问并建立对可见性至关重要的事前关联。

Every object has an intrinsic lock associated with it. 每个对象都有一个与之关联的固有锁。 By convention, a thread that needs exclusive and consistent access to an object's fields has to acquire the object's intrinsic lock before accessing them, and then release the intrinsic lock when it's done with them. 按照约定,需要对对象的字段进行独占且一致的访问的线程必须在访问对象之前先获取对象的固有锁,然后在完成对它们的锁定后释放固有锁。 A thread is said to own the intrinsic lock between the time it has acquired the lock and released the lock. 据称,线程在获取锁和释放锁之间拥有内部锁。 As long as a thread owns an intrinsic lock, no other thread can acquire the same lock. 只要一个线程拥有一个内在锁,其他任何线程都无法获得相同的锁。 The other thread will block when it attempts to acquire the lock. 另一个线程在尝试获取锁时将阻塞。

... ...

When a thread invokes a synchronized method, it automatically acquires the intrinsic lock for that method's object and releases it when the method returns. 当线程调用同步方法时,它将自动获取该方法对象的内在锁,并在方法返回时释放该内在锁。 The lock release occurs even if the return was caused by an uncaught exception. 即使返回是由未捕获的异常引起的,也会发生锁定释放。

... ...

Another way to create synchronized code is with synchronized statements. 创建同步代码的另一种方法是使用同步语句。 Unlike synchronized methods, synchronized statements must specify the object that provides the intrinsic lock [...] 与同步方法不同,同步语句必须指定提供内部锁的对象[...]

In your code, the current thread is not the owner of this , because you're not synchronizing - hence the exception. 在您的代码中,当前线程不是 this的所有者,因为您未同步-因此是异常。 You must synchronize - remember that wait() will release the monitor, then reacquire it before returning. 必须同步-请记住, wait()释放监视器,然后在返回之前重新获取它。

As a side-note, I'd strongly recommend against using a Thread object's monitor for synchronization, waiting, notifying etc - code within Thread already does that, so your code and its code could easily interfere with each other. 作为一个侧面说明,我强烈建议不要使用一个Thread对象的监视器进行同步,等待通知等-内码Thread已经这样做,那么您的代码和它的代码很容易相互干扰。 I'd recommend creating a separate object solely for the purpose of synchronization/wait/notify. 我建议仅出于同步/等待/通知的目的创建单独的对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM