当我尝试运行此程序时IllegalMonitorStateException

时间:2010-10-17 15:13:17

标签: java multithreading

public class ThreadTest
{
 public static Integer i = new Integer(0);

 public static void main(String[] args) throws InterruptedException
 {
  ThreadTest threadTest = new ThreadTest();
  Runnable odd = threadTest.new Numbers(1, "thread1");
  Runnable even = threadTest.new Numbers(0, "thread2");
  ((Thread) odd).start();
  ((Thread) even).start();
 }

 class Numbers extends Thread
 {
  int reminder;
  String threadName;

  Numbers(int reminder, String threadName)
  {
   this.reminder = reminder;
   this.threadName = threadName;
  }

  @Override
  public void run()
  {
   while (i < 20)
   {
    synchronized (i)
    {
     if (i % 2 == reminder)
     {
      System.out.println(threadName + " : " + i);
      i++;
      i.notify();
     }
     else
     {
      try
      {
       i.wait();
      }
      catch (InterruptedException e)
      {
       // TODO Auto-generated catch block
       e.printStackTrace();
      }
     }
    }
   }
  }
 }
}

4 个答案:

答案 0 :(得分:3)

您无法在i上进行同步,因为它会在您执行程序时发生变化。

由于Java中的Integer是不可变的,因此在执行i++ i之后将包含对另一个对象的引用,而不是您synchronized上的对象。因此,您无法在此新对象上调用wait() / notify(),因为这些方法只能在您synchronized上的对象上调用,否则您将获得IllegalMonitorStateException

您需要在执行期间不会更改的其他对象上进行同步。例如,您可以为此目的创建一个单独的对象:

public class ThreadTest { 
    public static Integer i = new Integer(0); 
    public static Object lock = new Object();
    ...
    class Numbers extends Thread { 
        ...
        @Override 
        public void run() { 
             ...
             synchronized (lock) {
                 ...
                 lock.notify();
                 ...
                 lock.wait();
                 ...
             }
        }
    }
}

答案 1 :(得分:1)

这一行:

i++;

相当于:

i = i + 1;

(由于自动装箱)变成了类似的东西:

i = new Integer(i.intValue() + 1);

因此,当您致电i.notify()时,您会在 i上同步,而不是新的。

我建议将i更改为普通的int变量,并创建一个单独的对象进行同步:

static int i = 0;
static Object iMonitor = new Object();

答案 2 :(得分:0)

正如文档所述,

时会抛出异常
  

当前线程不是对象监视器的所有者

它还说明了

  

此方法只应由作为此对象监视器所有者的线程调用。

这种情况可以通过

获得
  • 执行该对象的同步实例方法。
  • 执行在对象上同步的同步语句的主体。
  • 对于Class类型的对象,通过执行该类的同步静态方法。

您可以尝试从使用i的类中调用wait方法。这可以通过扩展课程并为notifywait编写两个新方法来完成。

答案 3 :(得分:-1)

您不能将wait()和notify()放在同一个同步块中,因为这只会导致死锁。确保只有wait和notify函数包含一个synchronized块,如下所示:

synchronized (i) {
    i.wait(); // or i.notify();
}