簡體   English   中英

'同步'真的只是語法糖嗎?

[英]Is 'synchronized' really just syntactic sugar?

我是多線程的新手,我編寫了這段代碼,通過同時運行線程增量並打印變量來打印數字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。

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

synchronized絕不是任何語法糖。 如果不使用synchronized關鍵字,就無法在Java中使用鎖。

在Java中鎖定中存在“語法糖”的地方, synchronized可以應用於塊(如上所述)和整個方法。 以下兩種方法在語義上大致相同:

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

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

那你為什么要做第二個版本而不是第一個呢?

  • 同步方法調用比普通的舊方法調用慢得多,比如大約一個數量級。 如果您的同步代碼不能保證始終執行(比如說它是在條件中),那么您可能不希望同步整個方法。
  • 同步方法保持鎖定比同步塊更長(因為所有方法設置/拆除代碼)。 上面的第二種方法將鎖定時間更短,因為設置和拆除堆棧幀所花費的時間不會被鎖定。
  • 如果使用同步塊,您可以更精確地控制您正在鎖定的內容。
  • (禮貌starblue同步塊可以用比其他對象this對於鎖定,讓你更加靈活的鎖定語義。

聽起來你的消息來源是錯的。 在編寫線程安全的代碼時, syncrhonized關鍵字很重要,並且可以正確使用。 聽起來你自己的實驗證明了這一點。

有關Java中同步的更多信息:

Java同步方法

Java鎖和同步語句

實際上,從Java 5開始,你(正式)在java.util.concurrent中有一套替代工具。 有關詳細信息,請參見此處 正如文章中詳述的那樣,Java語言級別提供的監視器鎖定模型具有許多重要的局限性,並且當存在一組復雜的相互依賴的對象和鎖定關系使得實時鎖定成為可能時,可能難以推斷。 java.util.concurrent庫提供了鎖定語義,這對於具有POSIX類系統經驗的程序員來說可能更熟悉

當然,“同步”只是句法糖 - 極端有用的句法糖。

如果你想要無糖的java程序,你應該直接在java字節代碼中編寫VM規范中引用的monitorentermonitorexitlockunlock操作8.13 Locks and Synchronization

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

synchronized語句計算對象的引用; 然后它嘗試對該對象執行鎖定操作,並且在鎖定操作成功完成之前不會繼續進行。 (鎖定操作可能會延遲,因為關於鎖定的規則可能會阻止主存儲器參與,直到某個其他線程准備好執行一個或多個解鎖操作。)執行鎖定操作后,將執行同步語句的主體。 通常,Java編程語言的編譯器確保在執行synchronized語句主體之前執行的monitorenter指令實現的鎖定操作與每當synchronized語句完成時由monitorexit指令實現的解鎖操作匹配,無論是否完成是正常的還是突然的

同步方法在調用時自動執行鎖定操作; 在鎖定操作成功完成之前,它的主體不會執行。 如果該方法是實例方法,則它鎖定與調用它的實例關聯的鎖(即,在執行方法體時將被稱為this的對象)。 如果方法是靜態的,它將鎖定與Class對象關聯的鎖,該Class對象表示定義方法的類。 如果方法的主體的執行正常或突然完成,則在同一鎖上自動執行解鎖操作。

最佳實踐是,如果變量由一個線程分配並由另一個線程使用或分配,那么對該變量的所有訪問都應該包含在synchronized方法或synchronized語句中。

盡管Java編程語言的編譯器通常保證鎖的結構化使用(請參見第7.14節“同步”),但無法保證提交給Java虛擬機的所有代碼都將遵循此屬性。 允許實現Java虛擬機,但不要求強制執行以下兩個保證結構化鎖定的規則。

設T為線程,L為鎖。 然后:

  1. 無論方法調用是正常還是突然完成,在方法調用期間由T在L上執行的鎖定操作的數量必須等於在方法調用期間由T在L上執行的解鎖操作的數量。

  2. 在方法調用期間的任何時候,自方法調用以來由T在L上執行的解鎖操作的數量超過了自方法調用以來由T在L上執行的鎖定操作的數量。

在不太正式的術語中,在方法調用期間,L上的每個解鎖操作必須與L上的某些先前鎖定操作匹配。

請注意,在調用調用方法時,Java虛擬機在調用synchronized方法時自動執行的鎖定和解鎖被認為是在調用方法的調用期間發生的。

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

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

如果要實例化方法中的塊,則同步必須使用在所有線程之間共享的對象的實例。 大多數人在第二點出錯,因為它出現在你的代碼中,其中同步似乎在不同的對象而不是單個對象上。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM