2012-02-08 50 views
26

,所以我用​​關鍵字測試。下面是我嘗試的例子:學習Java,使用synchronized關鍵字

public class MyTest { 
    static int i = 0; 
    public static void main(String[] args) { 
     new Thread(t1).start(); 
     new Thread(t2).start(); 
    } 

    private static void countMe(String name){ 
     i++; 
     System.out.println("Current Counter is: " + i + ", updated by: " + name); 
    } 

    private static Runnable t1 = new Runnable() { 
     public void run() { 
      try{ 
       for(int i=0; i<5; i++){ 
        countMe("t1"); 
       } 
      } catch (Exception e){} 

     } 
    }; 

    private static Runnable t2 = new Runnable() { 
     public void run() { 
      try{ 
       for(int i=0; i<5; i++){ 
        countMe("t2"); 
       } 
      } catch (Exception e){} 
     } 
    }; 
} 

當我運行它,從兩個線程調用countMe()方法的輸出產生以下輸出:

Current Counter is: 1 
Current Counter is: 2 
Current Counter is: 4 
Current Counter is: 5 
Current Counter is: 6 
Current Counter is: 7 
Current Counter is: 3 
Current Counter is: 8 
Current Counter is: 9 
Current Counter is: 10 

當我改變方法countMe()到:

private synchronized static void countMe(){ 
     i++; 
     System.out.println("Current Counter is: " + i); 
} 

我得到這樣的輸出:

Current Counter is: 1 
Current Counter is: 2 
Current Counter is: 3 
Current Counter is: 4 
Current Counter is: 5 
Current Counter is: 6 
Current Counter is: 7 
Current Counter is: 8 
Current Counter is: 9 
Current Counter is: 10 

雖然這給了我清醒的認識​​的目的,我想知道有沒有其他的原因還有,我們可以使用​​。或者我在這裏做了什麼,是唯一的艾森我們爲什麼需要使用這個​​關鍵字?

謝謝。

編輯:我很困惑與另一件事是,在第一輸出爲什麼櫃檯去3 7後這似乎有點不可能給我,但類似的結果確實發生每次我試圖,這是正常的?

+0

不能保證線程會交替。計數3的線程可能不會打印,直到另一個線程計數到7之後。 – 2012-02-08 16:00:46

+0

謝謝,但是像7之後如何能夠打印下一個3.我的意思是它可以打印6或甚至5,但這好像是新的方法調用在3歲之後沒有讀過我,這聽起來很奇怪。所以你認爲每當我必須使用來自2個或更多線程的相同代碼塊時總是使用同步應該是明智的? – 911TurboS 2012-02-08 16:04:29

+5

我建議你打印出該線程的名稱,它會在混淆的地方變得更加明顯。併發是一個複雜的話題,沒有簡單的答案。我的首選是使用參與者模式,儘可能在線程間分享盡可能少的數據。理想情況下,沒有。一個線程非常有效地執行代碼通常綽綽有餘。 – 2012-02-08 16:09:12

回答

24

兩件事情:

首先,這是不可能的同一對象的同步方法交錯兩個調用。當一個線程正在執行一個對象的同步方法時,所有其他線程調用同一對象的同步方法塊(掛起執行),直到第一個線程完成對象。

其次,當一個同步方法退出時,它自動地建立一個之前發生有用於相同對象的同步方法的任何後續調用關係。這保證了對所有線程都可見的對象狀態的更改。

同步方法能夠防止線程干擾和內存一致性錯誤一個簡單的策略:如果對象是一個多線程可見,所有讀取或寫入該對象的變量,通過同步的方法進行。 (一個重要的例外:一旦構造對象,最終字段在構造對象後無法修改,可以通過非同步方法安全地讀取)。

來源:http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

+0

vulkanino,那麼如果我使用訪問相同方法的線程,那麼每次使用synchronized都是明智的選擇。 – 911TurboS 2012-02-08 15:58:28

+0

+1,比我的答案要好... – Nim 2012-02-08 15:59:23

+0

@ 911TurboS在某些方法上使用'synchronized'對於正確同步多線程代碼是極其不足的。你必須意識到數據的一致性(可能還有很多其他的東西)。 – toto2 2012-02-08 16:23:18

11

Vulkanino給了一個很好的答案,你的主要問題,是因爲居然有我只解決您的問題約3印刷後7

3可在7後打印您的語句中的字節碼比Java代碼多得多。

我會擴大這一點。

你叫

System.out.println("Current Counter is: " + i); 

,並在Java代碼一行的發生,但真的發生的事情是創建一個字符串,然後該字符串傳遞給println。在實際將行寫入控制檯之前,println方法本身必須執行一些處理。

從概念上講,類似以下情況正在發生。

String printlnString = "Current Counter is: 3" 
--> maybe the other thread executes here 
System.out.println(printlnString); 
--> or maybe the other thread executes here 
i is now equal to 7 and the console has "Current Counter is: 7" 
println writes "Current Counter is: 3" to console 
+0

而不是寫計數器出來,寫入一個簡單的數組,然後當線程完成後,打印出陣列。 – cognacc 2013-04-03 14:45:12