java 防止并发修改异常的最佳方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10218883/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 00:04:58  来源:igfitidea点击:

Best way to prevent concurrent modification exception

javamultithreadinglistconcurrencyconcurrentmodification

提问by thatidiotguy

Here is some pseudo code as follows.

下面是一些伪代码,如下所示。

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;
    }
}

So essentially the problem is that other objects not listed in the code above call getSomeStuff() to get the list for read-only purposes. I am getting concurrentmodificationexception in the timer thread when this occurs. I tried making the getSomeStuff method synchronized, and even tried synchronized blocks in the timer thread, but still kept getting the error. What is the easiest method of stopping concurrent access of the list?

所以本质上的问题是上面代码中未列出的其他对象调用 getSomeStuff() 来获取列表以用于只读目的。发生这种情况时,我在计时器线程中收到了并发修改异常。我尝试使 getSomeStuff 方法同步,甚至尝试在计时器线程中同步块,但仍然不断收到错误消息。停止并发访问列表的最简单方法是什么?

回答by Eugene Retunsky

You can use either java.util.concurrent.CopyOnWriteArrayListor make a copy (or get an array with Collection.toArraymethod) before iterating the list in the thread.

在迭代线程中的列表之前,您可以使用java.util.concurrent.CopyOnWriteArrayList或制作副本(或使用Collection.toArray方法获取数组)。

Besides that, removing in a for-each construction breaks iterator, so it's not a valid way to process the list in this case.

除此之外,在 for-each 构造中删除会破坏迭代器,因此在这种情况下它不是处理列表的有效方法。

But you can do the following:

但是您可以执行以下操作:

for (Iterator<SomeClass> i = list.iterator(); i.hasNext();) {
    SomeClass next = i.next();
    if (need_to_remove){
       i.remove(i);                
    }
}

or

或者

for (int i = list.size() - 1; i >= 0; i--){            
    if (need_to_remove) {
        list.remove(i);                
    }
}

Also note, that if your code accesses the list from different threads and the list is modified, you need to synchronize it. For example:

另请注意,如果您的代码从不同的线程访问列表并且列表被修改,则需要对其进行同步。例如:

    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 

But note, that operations withing locks should be as short as possible.

但请注意,带锁的操作应该尽可能短。

回答by Adam Zalcman

You should make a copy of the list in getSomeStuff(). Publishing a reference to a private field like this makes it effectively public, so it isn't something you want to do anyway.

您应该复制getSomeStuff(). 像这样发布对私有字段的引用使其有效地公开,因此无论如何这不是您想要做的事情。

Also, consider returning a copy as an ImmutableListor at least as an unmodifiable list.

此外,考虑将副本作为ImmutableList或至少作为不可修改的 list 返回