2010-11-10 90 views
2

在下面兩個同步策略中,哪一個被優化(如在處理和生成的字節碼中)以及應該使用其中之一的場景。哪個更有效率?爲什麼?

public synchronized void addName(String name) 
{ 
     lastName = name; 
     nameCount++; 
     nameList.add(name); 
} 

public void addName(String name) { 
    synchronized(this) { 
     lastName = name; 
     nameCount++; 
     nameList.add(name); 
    } 

} 

還什麼是advisiable方式處理併發:使用利用使用JobUIJob API上述低電平方法

  • java.util.concurrent
    1. (如果在eclipse P中工作DE環境)

    感謝

  • +0

    問題標題不清楚(什麼更有效?)。 – Guillaume 2010-11-10 17:10:01

    +0

    這段代碼的目的是什麼?它是真實的代碼還是隻是一個例子?例如不是nameCount相同nameList.size()?並不是lastName只是nameList中的最後一個元素? nameList用於什麼?用一個簡單的鏈表結構替換整個事物似乎很容易,我可以自動CAS並且完全不需要同步 – 2010-11-11 01:08:58

    +0

    @Jed Wesley:這只是一個例子來理解上述方法之間的區別(根據性能,用例和字節碼生成)。 – Favonius 2010-11-11 05:30:02

    回答

    5

    哪一個被優化(如在處理和生成的字節代碼)

    根據this IBM DeveloperWorks Article Section 1,一個同步方法相比,同步塊時產生更少的字節碼。文章解釋了爲什麼。

    片段從文章:

    當JVM執行一個同步方法,正在執行的線程標識,所述方法的method_info結構具有ACC_SYNCHRONIZED標誌設置,則它自動獲取對象的鎖,調用該方法,並釋放鎖定。如果發生異常,線程會自動釋放該鎖。

    同步的方法塊,對 另一方面,繞過JVM的 內置支持用於獲取 對象的鎖和異常處理 和要求的功能是 明確地寫在字節碼。如果 您讀取了帶有同步塊的 方法的字節碼,您將會看到1235個附加的 操作來管理此功能。清單1只顯示了來電 同時生成一個synchronized方法 和synchronized塊:

    編輯解決的第一個評論

    爲了讓其他SOers信貸,這裏是爲什麼人會使用一個很好的討論同步。塊。我相信你可以找到更多有趣的討論,如果你搜索周圍:)

    Is there an advantage to use a Synchronized Method instead of a Synchronized Block?

    我個人還沒有使用同步。塊鎖定除this以外的另一個對象,但這是一個使用SOers指出關於同步。塊。

    +0

    感謝Kin U.根據鏈接,synchronized塊生成更多的字節碼,那麼使用它們有什麼用處。請給出一個用例,其中同步的'blocks'比同步的'methods'更有用(除了同步的粒度之外)。 – Favonius 2010-11-10 16:31:23

    0

    很難說,因爲這兩個代碼片段的arent等同。

    區別(缺少呼叫的同步添加)可能是重要的,它可能不是。從你給我們的不可能說的。

    +0

    感謝指出。 :)我有點離開了'add'方法。 – Favonius 2010-11-10 15:54:03

    +0

    他們爲什麼不相同? – khachik 2010-11-10 15:56:32

    5
    • 您更新的兩段代碼在語義上是相同的。但是,像第二部分中那樣使用同步塊允許您進行更多的控制,因爲您可以在不同的對象上進行同步,或者實際上不會同步不需要的部分方法。
    • 使用java.util.concurrent非常適合在任何可能的情況下使用同步原語,因爲它允許您在更高級別的抽象級別工作,並使用由非常熟練的人員編寫並密集測試的代碼。
    • 如果你在eclipse PDE中工作,使用它的API最可能是最喜歡的,因爲它與平臺的其餘部分相關聯。
    +0

    感謝您指出左邊的部分。我更新了代碼片段。 – Favonius 2010-11-10 15:57:44

    +0

    那麼,'語義相同'意味着爲它們生成的字節碼是相同的嗎?如果我比另一個更喜歡一個,會有任何性能開銷嗎? – Favonius 2010-11-10 16:05:39

    +0

    @Favonius:不,這意味着它們在同步方面會有相同的效果。我不知道它在性能方面是否有任何不同。擁有更多的字節代碼與此並不相關,因爲只有JIT編譯器生成的機器代碼纔是真正重要的。 – 2010-11-10 17:27:03

    1

    我知道這可能是一個例子,但是如果你打算編寫這樣的代碼 - 再想一想。

    對我來說,看起來您正在複製信息,除非您看到需要對代碼進行性能更改,否則不應該這樣做。 (你幾乎從不應該這樣做)。

    • 如果你真的需要這是在幾個線程中運行的代碼,我會使用Collections.synchronizedList將nameList放入同步列表中。
    • 姓氏應該是一個getter,它可以選擇列表中的最後一個元素。
    • nameCount應該是列表的大小。

    如果您現在所做的工作已經完成,您還必須將訪問權限同步到所有引用變量的位置,這會使代碼更難以讀取並且難以維護。

    2

    從效率的角度來看,這完全沒有關係。

    有塊的一點是你可以指定你自己的鎖。您可以選擇封裝在對象中的鎖,而不是使用this,結果是您可以更多地控制誰可以獲取鎖(因爲您可以使對象外部的鎖無法訪問)。

    如果您使用this作爲鎖(無論您是將方法同步還是使用該塊),程序中的任何內容都可以獲取對象上的鎖,並且更難以推斷出程序正在執行的操作。

    限制對鎖的訪問會使您在可判定性方面獲得巨大收益,這樣做確實比刪除某個字節碼更有好處。

    1

    你可以刪除所有鎖定:

    class Names { 
        AtomicReference<Node> names = new AtomicReference<Node>(); 
    
        public void addName(final String name) { 
        Node old = names.get(); 
        while (!names.compareAndSet(old, new Node(old, name))) { 
         old = names.get(); 
        } 
        } 
    
        public String getName() { 
        final Node node = names.get(); 
        return (node == null) ? null : node.name; 
        } 
    
        static class Node { 
        final Node parent; 
        final String name; 
    
        Node(final Node parent, final String name) { 
         this.parent = parent; 
         this.name = name; 
        } 
    
        int count() { 
         int count = 0; 
         Node p = parent; 
         while (p != null) { 
         count++; 
         p = p.parent; 
         } 
         return count; 
        } 
        } 
    } 
    

    這基本上是一個二極管驅動器堆棧實現。您可以獲取大小和當前名稱,並且可以輕鬆實現內容中的迭代器(雖然與示例中的相反)。根據您的需求,也可以使用其他可選的寫入時複製容器。