线程安全简单

时间:2014-11-02 12:06:23

标签: java multithreading

如果我在多线程环境中有一个组件列表,并且我在这个列表上做了除add之外的任何操作(在本例中我使用了列表中的synchronized)和get(组件调用的方法是thread-这是安全的吗?

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
}

2 个答案:

答案 0 :(得分:1)

在当前形式中,它不是线程安全的: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>();

然后你甚至可以完全删除同步。但是你应该知道这些含义:每次修改时列表的内容都将被复制(因此名称......)。这通常用于您经常迭代但很少修改的小集合的情况。 (所有形式的听众都是这里的经典例子)。

答案 1 :(得分:0)

看起来没问题,除非我不确定useComponents()中的迭代器行为,如果你同时向列表中添加元素。

您是否考虑过使用CopyOnWriteArrayList