2010-06-26 42 views
6

我是新來的多線程,我寫了這個代碼打印數字1-10000通過同時運行線程增量和打印變量。'同步'真的只是語法糖嗎?

下面是我使用的代碼:

package threadtest; 

public class Main{ 

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

    private static class Incrementer extends Thread{ 

     @Override 
     public void run(){ 
      while (true){ 
       synchronized(lock){ 
         if (i>=10000) 
          break; 
         i++; 
         System.out.println(i); 
       } 
      }    
     } 
    } 


    public static void main(String[] args) { 
     new Incrementer().start(); 
     new Incrementer().start(); 
     new Incrementer().start(); 
     new Incrementer().start(); 
     new Incrementer().start(); 
     new Incrementer().start(); 
    } 
} 

這工作 - 我寫了一個測試程序來檢查輸出和印刷數字是爲了準確1-10000。

我的問題是這樣的:我聽說​​只是語法糖。但是,如果不使用它,我似乎無法取得成功的結果。我錯過了什麼?

+6

它當然不是。你能否引用你聽過的消息來源? – BalusC 2010-06-26 16:52:00

+1

@BalusC:查看接受的答案。我認爲這些消息來源只是將同步*方法*稱爲語法糖 - 我不確定這是否完全正確,但至少與我的想法不矛盾。 @skaffman:對於我所知道的,我本來可以做到沒有'synchronized'這個錯誤:) – Cam 2010-06-26 17:12:24

+0

@incrediman'synchronized'在一個方法上或多或少與將方法的主體放在'synchronized(this) {...}'(或者一個靜態方法'synchronized(MyClass.class){...}')。 (它在檔案文件中的表現有所不同,但這通常不應該讓你擔心。) – 2010-06-26 17:38:15

回答

14

​​對於任何東西都不是語法糖。如果不使用​​關鍵字,則無法在Java中處理鎖定。

如果在Java中有一種類型的「語法糖」,那麼​​既可以應用於塊(如上所述),也可以應用於整個方法。以下兩種方法在語義上大致相同:

synchronized void method1() { 
    // ... do stuff ... 
} 

void method2() { 
    synchronized(this) { 
    // ... do stuff ... 
    } 
} 

那麼,爲什麼要做第二個版本而不是第一個呢?

  • 同步的方法調用比普通的舊方法調用慢得多,比如大約一個數量級。如果您的同步代碼不能保證始終執行(說它是在條件中),那麼您可能不想同步整個方法。
  • 同步方法鎖定的時間比同步塊長(因爲所有的方法設置/拆卸代碼)。上面的第二種方法將鎖定時間更短,因爲設置和拆除堆棧幀的時間不會被鎖定。
  • 如果你使用synchronized塊,你可以更精確地控制你正在鎖定的內容。
  • (致謝starblue同步塊可以使用除this以外的對象進行鎖定,這可以爲您提供更靈活的鎖定語義。
+0

謝謝,這很有道理。很高興有清除:) – Cam 2010-06-26 17:09:41

+0

有了塊,你可以使用一些私人對象作爲鎖,它可以防止來自外部代碼的干擾。 – starblue 2010-06-26 18:49:34

+0

好點。我會在答案中加上starblue。 – 2010-06-27 01:13:10

0

同步是最重要的概念之一,而在多線程環境中編程。 使用同步時,必須考慮發生同步的對象。 例如,如果一個靜態方法是將被同步,則同步必須使用

synchronized(MyClass.class){ 
//code to be executed in the static context 
} 

類級別如果在實例方法的塊是則同步必須使用的對象的實例同步哪些在所有線程之間共享。 大多數人在第二點出現問題,因爲它出現在您的代碼中,其中同步似乎位於不同的對象而不是單個對象上。

1

實際上,從Java 5開始,您(正式)在java.util.concurrent中有一組替代工具。有關更多詳情,請參閱here。正如文章中詳述的那樣,Java語言級別提供的監視器鎖定模型具有許多重大限制,並且當存在一組複雜的相互依賴對象和鎖定關係使得實時鎖定成爲現實可能性時,可能難以推理。 java.util.concurrent庫提供了鎖定語義,對於有類似POSIX系統經驗的程序員可能更熟悉。

1

當然,「synchronized」只是語法糖 - 極端有用的語法糖。

如果你想無糖Java程序,你應該直接在Java字節碼的monitorentermonitorexit解鎖操作中VM Specifications 8.13 Locks and Synchronization

有引用寫作是與每個對象關聯的鎖。 Java編程 語言不提供 執行單獨鎖定和解鎖 操作的方法;相反,它們是 由高級 隱式執行的構造,其總是安排將 這樣的操作正確地配對。 (Java的 虛擬機,但是,提供了單獨的 和monitorenter monitorexit 指令實現鎖定 和解鎖操作。)

同步的語句計算 參考的對象;然後 嘗試對該對象執行鎖定操作 ,並且不進行 ,直到鎖定操作已經成功完成 。 (A鎖 操作可以被推遲,因爲有關鎖可以防止主 存儲器從參與直到一些 其他線程準備執行一個 或多個解鎖操作。在 規則)的 鎖定操作之後已經被執行,則 同步語句的主體是 執行。通常,爲了使 Java編程語言的編譯器可確保 由 monitorenter指令來實現鎖定操作執行 之前的 synchronized聲明主體的執行匹配 通過 一個monitorexit指令每當執行解鎖操作 同步聲明完成, 是否完成正常或 突然。

自動同步方法 在調用 時執行鎖定操作;其機構不執行 ,直到鎖定操作成功完成 。如果該方法 是一個實例方法,它鎖定與實例爲 它被調用相關聯的 鎖(即,將所述方法的 體的執行期間被稱爲本 的 對象)。如果該方法是靜態的,則其 將鎖定與代表定義該方法的 類的 類對象關聯的鎖定。如果 執行方法的主體有 完成,通常或 突然,解鎖操作是 自動執行相同的 鎖。

最佳的做法是,如果一個變量是 以往由另一使用或分配一個線程和 被分配,那麼所有 訪問該變量應該被 括在同步方法或 同步語句。

雖然編譯爲Java 編程語言通常 保證結構使用鎖 (見第7.14節,「同步」), 但不能保證所有的代碼 提交給Java虛擬機 會服從這個屬性。 允許使用Java虛擬 機器的實現,但不要求 強制執行以下兩個保證結構化鎖定的規則兩個 規則。

讓T是一個線程,L是一個鎖。 然後:

  1. 的方法 調用期間對L-用T執行必須等於數量方法調用期間的 解鎖以T對L- 執行的操作鎖定操作的次數是否 方法調用完成 正常或突然。

  2. 在一方法調用期間沒有點可以被T對L-因爲 方法調用執行解鎖 操作的次數超過 號碼鎖操作的,因爲該方法調用對L-通過 Ť執行。

在不太正式術語中,方法調用 對L- 每解鎖操作必須在L.

注意,鎖定和解鎖 自動由Java 執行的一些前述鎖定 操作期間相匹配虛擬機在調用 同步方法時被認爲是 發生在調用方法的 調用中。