2016-02-19 83 views
2

這是類:這個類是不是線程安全的?

public class Saver { 

    private static ArrayList<String> nameCache = new ArrayList<>(); 

    public static synchronized void add(ArrayList<String> names) { 
     nameCache.addAll(names); 
    } 

    public static synchronized void save() { 
     for (String name : nameCache) { 
      //write name 
     } 
     nameCache.clear(); 
    } 
} 

不同的線程要調用保存並添加不同的訂單。 但是這個同步關鍵字會使這個進程線程安全嗎?

這是解決併發問題的正確方法,如果不是解決這個問題的好方法?

+0

是的。通過添加監視器(或其他機制),您可以使要保護線程安全的類安全,並且使用該類的線程將無法將它們全部一起使用。 – UDKOX

+0

如果在執行addAll時另一個線程修改了'names',則可能會發生'ConcurrentModificationException'。我還想知道是否存在潛在的可見性問題,因爲'nameCache'沒有被聲明爲'final'。 –

+0

@AndyTurner但nameCache是​​私人的? – user2997204

回答

2

這是線程安全的,因爲您不會有線程彼此踩在一起。同步類級鎖確保一次只有一個線程可以訪問列表,並且任何一個線程的更改都將對所有其他線程可見,因此hahn's answer是正確的(我的+1)。但相對於這個問題

這是解決併發問題,如果沒有這將是解決這一問題

答案是一個很好的方式方法不對,「這取決於上下文,但一般不會。「

每個線程都必須等待相同的鎖,並且當列表被保存時,每個想要添加內容的線程都必須等待。這可能會產生不可接受的性能問題,具體取決於負載和列表保存的頻率。

解決方案取決於具體情況,但通常儘量減少保存方法保持鎖定所需的時間。您可能需要使用保存方法創建列表的副本,只保留足夠長的鎖以創建副本,然後保存列表的副本,以便其他線程可以在副本保存的同時調用add方法。 (我是否保存過時的項目?什麼時候緩存需要過期?我是否在創建內存泄漏?等等),也是這樣的,它也是一種比你想象的更加充滿危險的東西一個技術實現細節往往與業務邏輯混雜在一起,這使得代碼更難以遵循。使數據和方法靜態引入全局狀態到應用程序中,這可能會使測試變得困難,使數據和方法屬於對象實例,並使用依賴注入將其注入到您想要的位置。

+0

真的很好的解釋!但是「每個想要添加內容的線程都必須等待」,訪問線程不能同時添加和刪除列表項,對吧?這就是爲什麼我同步兩種方法 – user2997204

+0

@ user2997204:正確的,這就是爲什麼我開始「hanh的答案是正確的。」 upvoted。 –

+0

好吧,無論如何感謝您的建議,使副本。 – user2997204

2

由於同步是在Saver類上完成的,每次只有一個Thread可以訪問nameCache