2013-03-23 93 views
2

我正在進入Java多線程。我對C/C++ pthreads非常熟悉,但是遇到了Java notify()wait()函數的問題。爲什麼會拋出IllegalMonitorStateException?

據我所知,IllegalMoinitorStateException只有當一個線程不「擁有」(又名未經同步)調用通知/等待。

當寫我的申請,我就遇到了這個問題。我分離下面的測試代碼的問題:

public class HelloWorld 
{ 
    public static Integer notifier = 0; 
    public static void main(String[] args){ 
     notifier = 100; 
     Thread thread = new Thread(new Runnable(){ 
      public void run(){ 
        synchronized (notifier){ 
          System.out.println("Notifier is: " + notifier + " waiting"); 
          try{ 
           notifier.wait(); 
           System.out.println("Awake, notifier is " + notifier); 
          } 
          catch (InterruptedException e){e.printStackTrace();} 
        } 
      }}); 
     thread.start(); 
     try{ 
       Thread.sleep(1000); 
      } 
     catch (InterruptedException e){ 
       e.printStackTrace(); 
      } 
     synchronized (notifier){ 
      notifier = 50; 
      System.out.println("Notifier is: " + notifier + " notifying"); 
      notifier.notify(); 
     } 
     } 
    } 

此輸出:

Exception in thread "main" java.lang.IllegalMonitorStateException 
     at java.lang.Object.notify(Native Method) 
     at HelloWorld.main(HelloWorld.java:27) 

相信我已經獲取的通知對象的鎖。我究竟做錯了什麼?

謝謝!

編輯:

從這個可能重複(Synchronizing on an Integer value),它似乎不是在整型同步,因爲這是很難確保你是在同一個實例進行同步是個好主意。因爲我正在同步的整數是全局可見的靜態整數,爲什麼我得到不同的實例?

+0

可能重複:HTTP://stackoverflow.com/questions/659915/synchronizing-on-an-integer-value – Cratylus 2013-03-23 19:26:59

+0

又一個暗示,因爲它已經有了答案:嘗試將通知設置爲最終。由於您爲通知程序指定了不同的值(對象),因此它不會編譯。 – 2013-03-23 20:07:11

回答

5

由於notifier = 50;要調用不同的對象上notifier.notify();

0

最初,當您在非主線程中調用同步notifier時,它引用堆中的對象,其內容爲0,因此線程擁有該對象。現在,在使用notifier.wait將非主線程放在wait後,控制權交給了主線程。在獲取包含值0的Integer對象的鎖之後,您使notifier引用包含值50的堆內存上的另一個對象,因爲notifier = 50;實際上等於notifier = new Integer(50),這意味着將創建一個新對象Integer並將其參考傳遞給notifier。現在,當線程看到notifier.notify時,它看起來主線程現在不再擁有它之前獲得的原始對象。所以IllegalMonitorStateException正在拋出。

0

只需添加更多的信息,你不應該在非最終目標上同步。

你需要一個恆定對象上同步。如果您正在分配(即改變物體)的任何對象上同步,則對象不是恆定的,一旦分配對象,並設法通知它,你會得到IllegalMonitorStateException。這也意味着,因爲它們在不同的對象上進行同步,所以多個線程將同時進入受保護的塊,並且競爭條件將發生。

因爲我同步的整數是全局visibile靜態整數,爲什麼我得到不同的實例?

當值分配給一個Integer它變成一個不同對象引用 - 即使它是static。你是不是改變相同的對象,因爲Integer不能突變。因此notifier = 50;將其分配給與notifier = 0;不同的對象。

如果你想使用,例如,一個恆定的對象,你可以使用一個AtomicBoolean

public static final AtomicInteger notifier = new AtomicInteger(0); 
    ... 
    synchronize (notifier) { 
     notifier.set(50); 
     ... 
    } 

這裏,AtomicInteger可以打上final,因爲它是同一個對象所有的時間。

欲瞭解更多信息,請參見:Why is it not a good practice to synchronize on Boolean?