2016-12-29 73 views
1

我想要有線程安全的同步集合(列表或集合)。我的集合將包含變量類的對象,它具有名稱,值等文件。現在讓我們說兩個線程想要修改列表中的對象(添加新的或刪除現有的對象)。什麼是最好的解決方案呢?我在stackoverflow上找到了這樣的smth。同步收集的變量

List<Variable> varList = Collections.synchronizedList(new ArrayList<Variable>()); 

void processList(List<Variable> varList, String name) 
{ 
    synchronized(varList) { 
     varList.stream().filter(o -> o.getName().equals(name)).findFirst().ifPresent(o -> o.setValue(100)); 
    // or 
    varList.remove(0); 
    // or 
    varList.add(new Variable()); 
    } 
} 

它會工作沒有任何問題嗎?還是有更好的解決方案?

+0

如果線程修改列表中的'Variable'對象那麼'Variable'類必須是線程安全的,而不是名單。 –

+0

這工作,列表將是線程安全的。 –

+0

如果你的Variable.class是可變的,那麼你可以在這個對象上共享狀態,這可能導致另一個「競爭條件」。 –

回答

2

最好的解決方案取決於用例。如果你需要一個有序的randomacces集合,那麼有3個選項。

1)你描述的那個(Synchronized List)。它的執行速度將與底部的Collection類型一樣快,並且整個Collection將被鎖定在Thread acces上。

除了Iterator的實現默認情況下不是Threadsafe,因此需要額外的threadSafty度量。 (一個提取器通常用於forEach, clear和其他默認(java 8)實現)。

2) The CopyOnWriteArrayList。這是唯一的ThreadSafe(包括具有快照功能的迭代器)randomAcces List在JDK中。

缺點是它在添加/刪除數據時複製數據。對於較小的集合來說,這可能不是問題,儘管基本規則是,當「讀取」操作比「寫入」操作多得多時,它是最好的解決方案。

3)Vector。雖然其'寫入'機制不需要複製備份數據,但是與'拷貝寫入清單'比較的'讀取'動作有點慢。

這個問題的一個缺點是Iterator也不是ThreadSafe。與Synchronized列表的區別在於,當有更多的Threads訪問Collections時,它將比SynchronizedList執行得更好。



Sumarry:

Synchronized List

  • 數低accesing線程的快速性能。
  • 迭代器並不線程上「讀」的行動
  • 副本上「寫」操作的備份數據(可能與大集合的問題)安全

CopyOnWriteArrayList

  • 快速的性能
  • 迭代器是線程安全的(使用快照功能)

Vector

  • 更快的性能(compaired到同步列表)具有大量accesing線程。
  • 迭代器不是線程安全的
+0

感謝您的回答。在我的情況下,表演並不那麼重要,所以我會慷慨地留在列表中。但似乎我還有一些問題:1.我想改變/修改列表中的對象(改變它們的字段,不僅刪除/添加新的元素)。根據我的問題的其他意見,我需要一些額外的保護?我到底該怎麼做? 2.你說迭代器操作對於List不是線程安全的。所以通過使用stream()和filter()方法找到對象不是一個好主意? 3.我是否真的需要同步列表,還是應該在同步塊中放置「正常」列表? –

+1

1)「變量」的同步需要在對象本身內進行處理。用List實現沒有辦法做到這一點。所以請首先解決這個問題2)當使用迭代器時,最簡單的方法是使用列表作爲鎖定對象的同步塊。 (例如'synchronized(varList){/ * do stuff * /}')。 3)如果你確保每次訪問列表時都使用同步塊,它(和前一部分)不會成爲問題。雖然如果性能不是問題,請使用'CopyOnWriteArrayList',因爲它完全是ThreadSafe(當然不包括對象本身!) – n247s

+0

再次感謝您的回覆。現在對我來說更加清晰。但還有一個問題。如果我想修改的對象總是從同一個列表中獲取,那麼對於單個變量實例是否真的需要另一個保護?因爲要獲取對象,線程需要傳遞同步(varList)語句,如果另一個線程在列表中工作,這將不可能。所以我認爲只需要額外的同步機制,如果我想訪問列表之外的那些對象,或者我錯了? –

1

我會做更簡單。您只需要在同步塊內的列表上進行所有修改,使用與鎖相同的列表對象。

例如,假設你有列表

List<Variable> varList; 

已經充滿了變量對象。現在,每次要訪問或在你的代碼修改這個列表中,您必須包裹片與varlist中的一個synchronized塊鎖定代碼,如下所示:

synchronized(varList) { 
     // here you access, add, delete, or modify the List 
     // for example: 
     if(varList.get(1) == 3) { 
      varList.remove(1); 
      varList.add(new Variable()); 
     } 
    } 

對於這一點,你必須確保兩個螺紋與相同 varlist中工作時,這是,varlist中爲兩個線程(諸如現場,或一個靜態變量)的共享變量。您也可以使用任何其他新對象作爲鎖,但始終確保它是兩個線程的通用對象(不是具有相同名稱的兩個變量指向內存中的不同對象)。 但我更喜歡使用列表本身作爲鎖,以便它使我的代碼更清楚,我知道塊我修改名單內。