以下是一些伪代码。
public class MyObject
{
private List<Object> someStuff;
private Timer timer;
public MyObject()
{
someStuff = new ArrayList<Object>();
timer = new Timer(new TimerTask(){
public void run()
{
for(Object o : someStuff)
{
//do some more stuff involving add and removes possibly
}
}
}, 0, 60*1000);
}
public List<Object> getSomeStuff()
{
return this.someStuff;
}
}
基本上问题是上面代码中未列出的其他对象调用getSomeStuff()来获取列表以用于只读目的。当发生这种情况时,我在计时器线程中得到concurrentmodificationexception。我尝试使getSomeStuff方法同步,甚至尝试在计时器线程中使用synchronized块,但仍然不断收到错误。停止并发访问列表的最简单方法是什么?
答案 0 :(得分:13)
在迭代线程中的列表之前,您可以使用java.util.concurrent.CopyOnWriteArrayList
或制作副本(或使用Collection.toArray
方法获取数组)。
除此之外,在for-each构造中删除会破坏迭代器,因此在这种情况下处理列表不是一种有效的方法。
但您可以执行以下操作:
for (Iterator<SomeClass> i = list.iterator(); i.hasNext();) {
SomeClass next = i.next();
if (need_to_remove){
i.remove(i);
}
}
或
for (int i = list.size() - 1; i >= 0; i--){
if (need_to_remove) {
list.remove(i);
}
}
另请注意,如果您的代码从不同的线程访问列表并且修改了列表,则需要同步它。例如:
private final ReadWriteLock lock = new ReentrantReadWriteLock();
final Lock w = lock.writeLock();
w.lock();
try {
// modifications of the list
} finally {
w.unlock();
}
.................................
final Lock r = lock.readLock();
r.lock();
try {
// read-only operations on the list
// e.g. copy it to an array
} finally {
r.unlock();
}
// and iterate outside the lock
但请注意,带锁的操作应该尽可能短。
答案 1 :(得分:4)
您应该在getSomeStuff()
中复制一份列表。发布对这样的私有字段的引用使其有效公开,因此它不是您想要做的事情。
另外,请考虑将副本作为ImmutableList
或至少作为unmodifiable list返回。