2015-10-26 185 views
1

創建線程敵對階級有效的Java第二版項目70喬希布洛赫解釋有關線程敵對如何避免在Java中

該類不是,即使所有的方法 調用包圍同時使用安全通過外部同步。螺紋 敵意通常修改靜態數據,而無需 同步

導致有人可以解釋我與例如它是如何不可能通過外部同步如果類中修改共享靜態數據沒有內部同步實現線程安全?

回答

4

引用假定每個對同一個對象實例上的類方法的調用都是同步的。例如,考慮下面的類:

public class Test { 
    private Set<String> set = new TreeSet<>(); 

    public void add(String s) { 
     set.add(s); 
    } 
} 

雖然它不是線程安全的,你可以安全地調用add方法是這樣的:

public void safeAdd(Test t, String s) { 
    synchronized(t) { 
     t.add(s); 
    } 
} 

如果safeAdd從多個線程調用同一t ,它們將是相互排斥的。如果使用不同的t,則更新獨立對象也很好。

但是考慮到我們聲明set爲靜態:

private static Set<String> set = new TreeSet<>(); 

這樣,甚至是不同的Test對象訪問共享的集合。因此,在這種情況下,Test實例上的同步將無法幫助,因爲同一個set仍可能會從不同的Test實例同時修改,這會導致數據丟失,隨機異常,無限循環或其他情況。所以這樣的班級將是線程敵對的。

+0

所以,現在唯一可能的方法是讓內部類同步它,即通過將add方法設置爲靜態同步來獲得對類對象本身的鎖定嗎? –

+0

@ArghyaSadhu,你仍然可以在類對象本身上進行外部同步,但這很醜陋,因爲該方法是非靜態的,因此它不會以不安全的方式修改全局狀態。更好的選擇是使用像ConcurrentSkipListSet這樣的併發友好的數據結構。 –

+0

@TagirValeev ...是的,因爲我們有一個併發友好的數據結構可供我們使用。如果有任何場景,即使外部類同步對象不會使這個類線程安全的情況下,我徘徊 –

1

或許一個人爲的例子,但這個類是不可能的外部同步,因爲該值是從類的外部訪問:

public class Example { 
    public static int value; 

    public void setValue(int newValue) { 
    this.value = newValue; 
    } 
} 

但是您同步二傳手的調用,你不能保證,一些其他線程不變value

+0

所以,如果我們在內部同步setter方法......它是否使類線程安全? –

+0

當你說「內部同步」時,你想到了什麼確切的變化? –

+0

「不可能同步外部,因爲價值是公開的」這聽起來很奇怪。它應該是「外部同步不會使代碼線程安全」。而這個理由與價值的公開沒有任何關係。 –

1

有人可以用例子解釋一下,如果類沒有內部同步修改共享靜態數據,外部同步如何實現線程安全是不可能的?

這不是不可能的。如果該類具有訪問全局(即static)數據的方法,則可以通過在全局鎖定上同步來實現線程安全。

但是強制調用者同步一個全局鎖上的線程仍然是線程敵對的。大型全局鎖定可能是多線程應用程序中的嚴重瓶頸。作者希望你做的是設計你的類,這樣客戶端就可以爲每個類的每個實例使用一個單獨的鎖。