Java 列出导致ConcurrentModificationException的原因

Java 列出导致ConcurrentModificationException的原因,java,Java,编辑:显然,该问题已被标记为的重复项,即使我的问题是关于如何在一个线程上安全地使用列表,而另一个线程正在修改列表,并且链接的问题是关于如何在迭代过程中删除列表 问题: 我在类中有一个列表对象。此类还包含一个返回对象列表的getObjects()方法 我有两个线程处理这个列表。一个线程时不时地添加对象,另一个线程处理对象并在完成后将其从列表中删除。当我在线程2中迭代列表时,问题就出现了,而线程1同时向列表中添加了一个对象 我试图通过让线程2只处理列表的一个副本而不是实际的副本来解决这个问题。使用L

编辑:显然,该问题已被标记为的重复项,即使我的问题是关于如何在一个线程上安全地使用列表,而另一个线程正在修改列表,并且链接的问题是关于如何在迭代过程中删除列表

问题: 我在类中有一个
列表对象
。此类还包含一个返回对象列表的
getObjects()
方法

我有两个线程处理这个列表。一个线程时不时地添加对象,另一个线程处理对象并在完成后将其从列表中删除。当我在线程2中迭代列表时,问题就出现了,而线程1同时向列表中添加了一个对象

我试图通过让线程2只处理列表的一个副本而不是实际的副本来解决这个问题。使用
List newList=getObjects()。在这里,我假设因为newList是一个不同的列表(虽然有相同的对象),所以我可以安全地迭代它并使用对象。但是,这似乎仍然会导致异常,因为(我假设)
=
操作符只是将newList作为对象列表的引用。我仍然在迭代对象列表

问题: 所以我的问题是:如何安全地处理线程2中列表中的对象

代码:尽管我怀疑它对回答这个问题有用,但代码如下(我删除了所有与这个问题无关的不必要的杂乱内容)

公共一级
{
列出对象;
公共一号
{
objects=newarraylist();
}
公共无效添加对象(对象)
{
对象。添加(对象);
}
公共列表getObjects()
{
归还物品;
}
}
公共二班
{
私家车;
公共二(一)
{
这个1=1;
}
public void processObjects()
{
List newList=one.getObjects();
for(Object Object:newList)//导致错误,因为在迭代过程中,线程1将对象添加到对象列表中。
{
//多斯塔夫和这个物体在一起。
}
一个.getObjects().removeAll(newList);
}
}

在这种情况下,使用
BlockingQueue
的实现会更好,其中一个线程将对象推入,另一个线程弹出并处理它们。它还将确保线程之间对象的安全发布。如果您仍然想使用
列表
,请查看
CopyOnWriteArrayList
@Makoto“副本”并不是此列表的副本。它指的是单线程代码。虽然我假设还有其他一些是这个的复制品,并且涉及到
CopyOnWriteArrayList
或者一些读写锁技巧(我还没有搜索到)。要指出一个基本(重要)错误:
List newList=one.getObjects()不创建列表的副本。它只是创建对同一列表的新引用。类似于
List newList=newarraylist(one.getObjects()),它将创建一个真正的新列表,但对于多线程代码,即使这样也不够!是的,创建副本可能会导致异常变得罕见(甚至消失)。但是仍然不能保证在创建副本时另一个线程不会添加新元素,这仍然可能导致不一致的状态。这里的“最佳”解决方案取决于许多因素(列表的大小、修改或读取的频率、性能要求等)。一个简单的解决方案是将列表设为一个
objects=newcopyonwritearraylist(),但当列表较大和/或频繁修改时,这可能不合适。在多线程代码中,创建列表副本是不够的,因为第二个线程可能永远看不到第一个线程添加的对象,因为您没有使用任何类型的同步。如果使用某种形式的同步,Java内存模型只保证线程之间的内存可见性。如果更改
objects=newarraylist()
to
objects=newcopyonwritearraylist(),它会工作,因为
CopyOnWriteArrayList
确保了内存可见性,并且您不必在第二个线程中创建副本。在这种情况下,使用
BlockingQueue
的实现会更好,其中一个线程将对象推入,另一个线程弹出并处理它们。它还将确保线程之间对象的安全发布。如果您仍然想使用
列表
,请查看
CopyOnWriteArrayList
@Makoto“副本”并不是此列表的副本。它指的是单线程代码。虽然我假设还有其他一些是这个的复制品,并且涉及到
CopyOnWriteArrayList
或者一些读写锁技巧(我还没有搜索到)。要指出一个基本(重要)错误:
List newList=one.getObjects()不创建列表的副本。它只是创建对同一列表的新引用。类似于
List newList=newarraylist(one.getObjects()),它将创建一个真正的新列表,但对于多线程代码,即使这样也不够!是的,创建副本可能会导致异常变得罕见(甚至消失)。但是仍然不能保证在创建副本时另一个线程不会添加新元素,这仍然可能导致不一致的状态。这里的“最佳”解决方案取决于许多因素(列表的大小、修改或读取的频率、性能要求等)。一个简单的解决方案是将列表设为一个
objects=newcopyonwritearraylist(),但当列表较大和/或频繁修改时,这可能不合适。在多线程代码中,仅创建列表副本是不够的,因为
public class One
{
    List<Object> objects;

    public One()
    {
        objects = new ArrayList<Object>();
    }
    public void addObject(Object object)
    {
        objects.add(object);
    }
    public List<Object> getObjects()
    {
        return objects;
    }
}

public class Two
{
    private One one;
    public Two(One one)
    {
        this.one = one;
    }
    public void processObjects()
    {
        List<Object> newList = one.getObjects();
        for(Object object : newList) //Causes error because during iteration thread 1 adds an object to the objects list.
        {
          //dostuff with the object.
        }
        one.getObjects().removeAll(newList);
    }
}