如果我在多线程环境中有一个组件列表,并且我在这个列表上做了除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
}
答案 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?