[英]Thread-safety simple
如果我在多線程環境中有一個組件列表,並且對該列表進行了除添加之外的任何操作(在這種情況下,我在列表上使用關鍵字sync)和獲取(一個組件調用的方法是線程安全的),那是線程安全的嗎?
public class Test {
private final ArrayList<Component> myContainer = new ArrayList<Component>();
public void add(Component){
synchronized(myContainer){
myContainer.add(Component)
}
}
public void useComponents()
{
for(Component c : myContainer)
c.use(); // method thread-safe
}
// no other operations on myContainer
}
在當前形式中,它不是線程安全的: useComponents
方法可以由一個線程執行。 同時,另一個線程可以調用add
,從而在迭代集合時修改集合。 (此修改可能發生在兩次調用c.use()
,因此use()
方法是線程安全的這一事實在這里無濟於事)。
嚴格來說,這甚至不限於多線程 :如果c.use()
內部調用了test.add(someOtherComponent)
(即使它是在同一線程中完成的),這將拋出ConcurrentModificiationException
,因為同樣,該集合是在迭代時修改。
可以通過簡單地將迭代包裝到synchronized
塊中來實現線程安全(無需再次進行安全修改):
public void useComponents()
{
synchronized (myContainer)
{
for(Component c : myContainer)
c.use(); // method thread-safe
}
}
但是,這仍然會留下ConcurrentModificationException
的可能性。 很有可能c.use()
調用不會(也不應該)修改組件所包含的集合(否則,人們可能會質疑設計)。
如果要允許c.use()
調用來修改集合,則可以用CopyOnWriteArrayList
替換集合:
private final List<Component> myContainer =
new CopyOnWriteArrayList<Component>();
然后您甚至可以完全刪除同步。 但是您應該意識到其中的含義:列表的內容將在每次修改期間復制 (因此,名稱為...)。 通常在您有一個經常被迭代但很少修改的小型集合的情況下使用。 (這里的各種形式的監聽器都是一個典型的例子)。
看起來還可以,除非您不確定是否要同時向列表中添加元素,但我不確定useComponents()中的迭代器行為。
您是否考慮過使用CopyOnWriteArrayList ?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.