为什么我的代码抛出IllegalMonitorStateException?

问题描述:

我想了解线程如何在Java中工作。因此,我写了一小段代码,我创建了两个线程,一个打印所有奇数和其他打印偶数,并尝试在它们之间进行同步,以便所有数字都按顺序打印。为什么我的代码抛出IllegalMonitorStateException?

public class Main 
{ 

    public static void main (String args[]){ 

     Main lock = new Main(); 

     Test t1 = new Test(1, lock); 

     Test t2 = new Test(2, lock); 

     synchronized(lock){ 

      (new Thread(t1)).start(); 
      (new Thread(t2)).start(); 

     } 


    } 

} 

public class Test implements Runnable { 


    int n; 
    Main lock; 

    public Test(int newN, Main lockObj){ 
     this.n = newN; 
     this.lock = lockObj; 
    } 


    public void run() { 


     while(true){ 

      if (n != 1){ 
       try { 
        lock.wait(); 
       } catch (InterruptedException e) { 

        e.printStackTrace(); 
       } 
      } 

      n = n + 2; 

      System.out.println(n); 

      lock.notify(); 

     } 


    } 


} 

可有人请帮助理解潜在的问题是什么?

+0

将'Main'的实例用作锁对象有点奇怪。它没有伤害,但更传统的说'对象锁定=新对象()'。当你开始与其他开发人员合作时,更传统就变得很重要。如果你的代码看起来不奇怪,他们会更乐意与你一起工作。 – 2015-02-10 18:13:17

+0

重要的是要注意,wait()必须始终在严格循环内调用,以检查正在等待的条件。该方法的[文档](http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait--)具有完整的详细信息和示例。 – VGR 2015-02-10 19:25:24

您有三个线程,main线程,t1t2。线程拥有锁可以使用动作notify,notifyAllwait。在这种情况下main线程拥有的锁不是t1t2

您需要在每个线程的run方法内同步以便能够wait


下面是我通过synchronizingrun方法意味着一个例子。

在启动线程后,从run方法发生的任何操作都将由该线程运行完成。所以,当你尝试和wait你会想要确保线程拥有锁,你可以通过在run方法中同步它来做到这一点。

public void run() { 
    synchronized(lock){ 
     while(true){ 
      if (n != 1){ 
       try { 
        lock.wait(); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 
      n = n + 2; 
      System.out.println(n); 
      lock.notify(); 
     } 
    } 
} 

正如你可以看到我的run方法中的同步,这样t1t2自己锁在通知或等待。

+0

我尝试从'main'线程中移除锁并在Test中创建一个静态锁(Main类),但得到了相同的结果。 考虑到班级结构,我如何才能解决特定问题的任何想法都保持不变? – user2560730 2015-02-10 17:55:31

+1

正如我最后一句话所说。你必须在每个独立线程的run方法中同步。不在'main'方法中。 – 2015-02-10 18:13:08

+0

不确定你的意思是“主线拥有锁”。这是一种可能性,但由于主线程与另外两个线程不同步,并且由于它只在短时间内拥有锁定,所以主线程已经很可能已经释放了锁并在任何一个之前终止两个线程输入Test#run()方法。 – 2015-02-10 18:20:51

这可能不会帮助您理解低级别的线程机制,但对于您的情况,使用CyclicBarrier与barrierAction是有意义的。

使两个踏板等待障碍处理后n = n + 2,让barrierAction sysout双线程当前的n,重复。