2011-12-26 43 views
0
import java.util.*; 
import java.io.*; 
import java.util.regex.*; 

class ZiggyTest2 extends Thread{ 

    String sa; 

    public ZiggyTest2(String sa){ 
     this.sa = sa; 
    } 

    public void run(){ 
     synchronized(sa){ 
      while(!sa.equals("Done")){ 
       try{ 
        sa.wait(); 
       }catch(InterruptedException is){System.out.println("IE Exception");} 
      } 
     } 

     System.out.println(sa); 
    } 
} 

class Test{ 
    private static String sa = new String("Not Done"); 

    public static void main(String[] args){ 
     Thread t1 = new ZiggyTest2(sa); 
     t1.start(); 

     synchronized(sa){ 
      sa = new String("Done"); 
      sa.notify(); 
     } 
    } 
} 

當我運行上面的程序,我得到以下異常:線程併發 - 同步和鎖定。

Exception in thread "main" java.lang.IllegalMonitorStateException 
     at java.lang.Object.notify(Native Method) 
     at Test.main(ZiggyTest2.java:35) 

幾個問題:

  • 爲什麼拋出:IllegalMonitorStateException?由於Test.sa被分配給一個新的String對象,因此我期待ZiggyTest2線程無限期地等待,因爲sa.notify()將在與ZiggyTest2中使用的鎖不同的鎖上被調用。

  • 在上面的示例中,wait()& notify()在「sa」對象上被調用。說自己調用notify()並使用對象調用notify()/ wait()(即sa.wait()和sa.notify()會有什麼不同?

  • 在Test類中,synchronized塊是否具有sa對象的鎖定,sa對象是靜態的,但在ZiggyTest2類中,同步塊使用相同的sa對象引用,但使用非靜態引用?鑑於一個是靜態的,另一個不是,他們是否仍然使用相同的鎖?

回答

2

當你執行

sa = new String("Done"); 

你不sa改變字符串的參考文獻內容。您將一個新的String實例(一個新對象)分配給sa。字符串是不可變的。改變它們的價值是不可能的。

這意味着您在sa上同步(第一個對象:「Not Done」),然後將一個新對象分配給sa(第二個對象:「完成」),並在此第二個對象上調用notify。由於您沒有在第二個對象上進行同步,但是在第一個對象上,您會收到IllegalMonitorException。只有當你擁有對象上的固有鎖時,才允許在對象上調用通知。這就是爲什麼鎖應始終是最終的。

調用notify()相當於調用this.notify()。因此,this.notify()sa.notify()只是在兩個不同的對象上調用notify()。第一個將通知等待this的線程,第二個將通知等待sa的線程。

變量是靜態還是不靜態並不重要。鎖被關聯到對象,而不是它的引用。

+0

謝謝@JB。優秀的解釋。 – ziggy 2011-12-26 14:54:18

+0

但是,即使您在主函數的同步塊中採用全新的字符串引用(根本不使用_sa_),錯誤仍然存​​在! – Santosh 2011-12-26 15:03:32

+0

你是什麼意思?我解釋說你必須擁有對象監視器來調用對象的通知。如果調用someObject.notify(),則必須在調用synchronized(someObject)之前(並且沒有釋放當然的鎖) – 2011-12-26 15:09:13