IllegalMonitorStateException在其run方法内的一个线程上调用wait()(没有synchronized块)

时间:2014-06-29 18:50:22

标签: java multithreading exception

我命令确保在套接字关闭时停止从套接字读取数据的线程(由于socket.isClosed()无法按预期工作)我写了一个“心跳”来检查是否套接字仍然是开放的。方法startHeartbeat()在BufferedReader开始读取套接字之前调用,并且仅在isClosed()为假时才开始读取。

这里有一些同步方法,但与其他类似问题不同,wait()调用不属于其中一种方法。这是基本代码:

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只是一个发出的常量字符串(post()方法只是将对象写在BufferedWriter上)而PULSE是节拍之间的时间。

我不确定为什么这里有IllegalMonitorStateException,在阅读各种线程后(我理解为什么他们有异常)并阅读API以获取异常。谁能告诉我这里做错了什么?

1 个答案:

答案 0 :(得分:5)

  

有人能告诉我这里做错了吗?

是的 - 你没有在你正在等待的对象上进行同步。 API documentation非常明确:

  

抛出:
  IllegalMonitorStateException - 如果当前线程不是对象监视器的所有者。

理解“对象监视器的所有者”基本上意味着“使用该对象处于同步块中”非常重要。同步块在开始时获取相关对象的监视器,并在结束时释放它。来自Java tutorial on synchronization

  

同步是围绕称为内部锁或监视器锁的内部实体构建的。 (API规范通常将此实体简称为“监视器”。)内部锁在同步的两个方面都发挥作用:强制对对象状态进行独占访问,并建立对可见性至关重要的先发生关系。

     

每个对象都有一个与之关联的内在锁。按照惯例,需要对对象字段进行独占和一致访问的线程必须在访问对象之前获取对象的内部锁,然后在完成它们时释放内部锁。据说一个线程在获得锁定和释放锁定之间拥有内在锁定。只要一个线程拥有一个内部锁,没有其他线程可以获得相同的锁。另一个线程在尝试获取锁时会阻塞。

     

...

     

当线程调用synchronized方法时,它会自动获取该方法对象的内部锁,并在方法返回时释放它。即使返回是由未捕获的异常引起的,也会发生锁定释放。

     

...

     

创建同步代码的另一种方法是使用synchronized语句。与synchronized方法不同,synchronized语句必须指定提供内部锁[...]

的对象

在您的代码中,当前线程不是 this的所有者,因为您没有同步 - 因此是例外。您必须同步 - 请记住wait()释放监视器,然后在返回之前重新获取它。

作为旁注,我强烈建议使用Thread对象的监视器进行同步,等待,通知等 - Thread中的代码已经这样做了,所以你的代码和代码很容易互相干扰。我建议仅为了同步/等待/通知而创建一个单独的对象。