簡體   English   中英

線程安全簡單

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM