2008-10-02 54 views
9

當設計一個集合類時,是否有任何理由不私自實現鎖定以使其線程安全?還是我應該把這個責任交給收藏品的消費者?使您的收藏線程安全?

+1

如果你使你的集合不可變,它將天生就是線程安全的,沒有任何人需要鎖定它。 – Juliet 2010-09-26 15:22:56

回答

13

是否有任何理由不實施私人鎖定以使其線程安全?

這取決於。你的目標是編寫一個由多線程訪問的集合類嗎? 如果是這樣,請使其線程安全。如果不是,不要浪費你的時間。這種事情是人們在談論'過早優化'時提到的東西

解決您遇到的問題。不要試圖解決你認爲將來可能會有幾年的未來問題,因爲你看不到未來,你總會錯誤的。

注意:您仍然需要以可維護的方式編寫代碼,例如確實需要來爲集合添加鎖定,但這不會非常困難。我的觀點是「不實現你不需要和不會使用的功能」

+2

如果你關心的是多線程性能,那麼不確定我是否會購買它並不是很難「的一部分,很簡單,但很難做到,請看看ConcurrentHashMap的代碼和HashMap。 – 2008-10-03 04:29:15

2

我個人會把它留給消費者。它會使你的集合類更通用。

2

只是要你的文檔,你不使它線程安全的,離開它了,或者如果你的應用程序,你希望它是線程安全的,使其線程安全,請注意您的文檔,它在清澈。唯一的規則是記錄它。除此之外,讓你的班級,如果其他人想要使用它,他們可以。

0

這將使得它不可能同時從多個線程訪問集合,即使你知道你觸摸元素沒有被任何人。

一個例子是一個帶有基於整數的索引存取器的集合。每個線程可以從它的id中知道它可以訪問哪些索引值,而不用擔心臟讀/寫操作。

另一種情況下,你會得到不必要的性能影響時,數據只從收集閱讀並沒有寫入到會。

2

如果我在尋找一個集合類,我需要線程安全的能力和你的類沒有他們,我馬上要跳到下一個產品在那裏,看看他們提供什麼樣的。你的收藏將不會再引起我的注意。

請注意開始處的「If」。有些顧客會想要,有些顧客不會,有些顧客不會關心。如果你打算爲消費者建立一個工具包,那麼爲什麼不提供這兩種品種呢?這樣我可以選擇使用哪一個,但是如果我想要線程安全,您仍然需要我的注意力,我不必親自編寫它。

3

收集類需要儘可能快。因此請將鎖留出。

調用代碼會知道哪裏最好鎖謊言集合類沒有。在最糟糕的情況下,應用程序將不得不添加一個額外的鎖,這意味着發生了兩次鎖,從而使其發生了雙倍的打擊。

1

不使線程安全的主要原因是性能。線程安全代碼比非安全代碼慢100倍,所以如果你的客戶端不需要這個功能,那是非常大的浪費。

0

我同意把它留給消費者是正確的做法。如果提供給消費者更多的靈活性,以確定Collection實例是同步還是另一個對象已同步。例如,如果您有兩個需要更新的列表,則可以使用單個鎖將它們放入單個同步塊中。

0

如果您創建了一個集合類,請不要讓它成爲線程安全的。很難做到正確(例如正確和快速),並且當你做錯了(heisenbugs)時,消費者的問題很難調試。

,不要成爲囚,實現集API中的一個,並使用Collections.synchronizedCollection(yourCollectionInstance)獲取線程安全的實現,如果他們需要它。

只需引用相應的集合。在你的類javadoc中的synchronizedXXX方法;它會清楚地表明您已經考慮到設計中的線程安全性,並確保消費者擁有線程安全的選項。

1

請注意,如果您嘗試使任何類都是線程安全的,則需要決定常見的使用場景。

例如,在一個集合的情況下,只是讓所有的屬性和方法單獨使用線程安全可能對於消費者來說不夠好,因爲首先讀取計數,然後循環或類似的操作不會執行如果讀數後計數發生了變化,這很好。

1

使集合線程安全是殺死Java的Vector和Hashtable類的原因。如前所述,客戶端將其封裝在線程安全包裝器中,或者在方法的子集上同步數據訪問比在每次訪問類時採取同步命中要容易得多。幾乎沒有人使用Vector或Hashtable,如果他們這樣做,他們會被嘲笑,因爲他們的替換(ArrayList和HashMap)世界更快。這是不幸的,因爲我(來自C++背景)更喜歡「Vector」名稱(STL),但ArrayList在這裏停留。

1

基本上,將您的集合設計爲線程安全的,並在您的類的兩個方法中實現鎖定:lock()和unlock()。在任何需要的地方打電話給他們,但讓他們空着然後對實現lock()和unlock()方法的集合進行子類化。兩個類的價格爲一個。

1

一個非常好的理由不使您的集合成爲線程安全的,這是爲了提高單線程性能。示例:Vector上的ArrayList。將線程安全推遲到調用方允許通過避免鎖定來優化未同步的用例。

使您的集合成爲線程安全的一個很好的理由是提高了多線程性能。示例:HashMap上的ConcurrentHashMap。因爲CHM內化了多線程問題,所以它可以比外部同步更有效地分割鎖定以獲得更大的併發訪問。

0

這是一個很好的開始。

thread-safe-dictionary

但你會發現你失去的收藏品的一大特點 - 枚舉。你不能對線程安全的枚舉器,這是不可行的,除非你實現自己的枚舉器,它將實例鎖持回到集合本身。我懷疑這會造成重大瓶頸和潛在的僵局。

6

線程安全集合可能是騙人的。 Jared Par發佈了一些關於線程安全集合的有趣文章:

問題是有幾個 級別的線程安全集合。我 發現,當大多數人說的線程 安全集合他們真正的意思 「的集合,將不會 修改和訪問從多個線程 時損壞」

...

但是,如果建立一個數據線程安全 列表非常簡單,爲什麼Microsoft 在 框架中添加這些標準集合?

回答:ThreadSafeList是一個幾乎不可用的類,因爲 設計將您引導到錯誤的 代碼的路徑。

在這個設計中的缺陷不是 顯然直到你檢查如何列表 是常用的。例如,取 下面的代碼,它試圖從 列表中抓取第一個元素(如果有的話) 。

static int GetFirstOrDefault(ThreadSafeList<int> list) { 
    if (list.Count > 0) { 
     return list[0]; 
    } 
    return 0; } 

此代碼是一個典型的競爭條件。考慮列表中只有一個>元素的情況。如果另一個線程刪除了if語句和return語句之間的元素,則return語句將引發異常,因爲它試圖訪問列表中的無效索引。即使ThreadSafeList是數據線程安全的,沒有什麼保證整個下次調用一個調用的返回值的有效性同一個對象

http://blogs.msdn.com/b/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx

http://blogs.msdn.com/b/jaredpar/archive/2009/02/16/a-more-usable-thread-safe-collection.aspx

0

隨着JDK 5,如果你需要一個線程安全的集合,我會先看看java.util.concurrent中已經實現的集合是否可以工作。作爲Java併發實踐的作者指出(包括寫大部分類的人)正確實現這些是非常困難的,特別是如果性能很重要。

報價http://download.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html

併發集合

除了隊列,此包提供 集合實現設計 用於多線程上下文: 的ConcurrentHashMap, ConcurrentSkipListMap, ConcurrentSkipListSet, 的CopyOnWriteArrayList,和 CopyOnWriteArraySet。當許多線程 預期訪問給定 集合,ConcurrentHashMap的是 通常優於同步 HashMap和一個ConcurrentSkipListMap 通常優選將 同步TreeMap中。 A 當 預期讀取次數和 遍歷次數遠遠超過 次數時,CopyOnWriteArrayList優於 同步ArrayList。