Java 在列表迭代时修改列表
当我测试自己的答案以获得问题的输出时,我得到了给定列表内容的以下输出:Java 在列表迭代时修改列表,java,iteration,concurrentmodification,Java,Iteration,Concurrentmodification,当我测试自己的答案以获得问题的输出时,我得到了给定列表内容的以下输出: // Add some strings into the list list.add("Item 1"); list.add("Item 2"); list.add("Item 3"); list.add("Item 4"); public class Main { public static void main(String[] args) throws InterruptedException {
// Add some strings into the list
list.add("Item 1");
list.add("Item 2");
list.add("Item 3");
list.add("Item 4");
public class Main {
public static void main(String[] args) throws InterruptedException
{
final ArrayList<String> list = new ArrayList<String>();
list.add("Item 1");
list.add("Item 2");
list.add("Item 3");
list.add("Item 4");
Thread thread = new Thread(new Runnable()
{
@Override
public void run ()
{
for (String s : list)
{
System.out.println(s);
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
});
thread.start();
Thread.sleep(2000);
list.remove(0);
}
}
输出:
Item 1
Item 2
Exception in thread "Thread-0" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at com.akefirad.tests.Main$1.run(Main.java:34)
at java.lang.Thread.run(Thread.java:745)
Item 1
Item 2
但如果使用以下列表:
// Add some strings into the list
list.add("Item 1");
list.add("Item 2");
list.add("Item 3");
输出:
Item 1
Item 2
Exception in thread "Thread-0" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at com.akefirad.tests.Main$1.run(Main.java:34)
at java.lang.Thread.run(Thread.java:745)
Item 1
Item 2
输出中没有异常,仅打印前两项。
有人能解释它为什么会这样吗?谢谢
注:代码为
编辑:我的问题是为什么我没有打印第三项(意味着列表被修改),而没有例外
编辑代码生成异常,请注意列表内容:
// Add some strings into the list
list.add("Item 1");
list.add("Item 2");
list.add("Item 3");
list.add("Item 4");
public class Main {
public static void main(String[] args) throws InterruptedException
{
final ArrayList<String> list = new ArrayList<String>();
list.add("Item 1");
list.add("Item 2");
list.add("Item 3");
list.add("Item 4");
Thread thread = new Thread(new Runnable()
{
@Override
public void run ()
{
for (String s : list)
{
System.out.println(s);
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
});
thread.start();
Thread.sleep(2000);
list.remove(0);
}
}
公共类主{
公共静态void main(字符串[]args)引发InterruptedException
{
最终ArrayList=新ArrayList();
列表。添加(“第1项”);
列表。添加(“第2项”);
列表。添加(“第3项”);
列表。添加(“第4项”);
Thread Thread=新线程(new Runnable()
{
@凌驾
公开作废运行()
{
用于(字符串s:列表)
{
系统输出打印项次;
尝试
{
睡眠(1000);
}
捕捉(中断异常e)
{
e、 printStackTrace();
}
}
}
});
thread.start();
《睡眠》(2000年);
列表。删除(0);
}
}
您试图重现的行为高度依赖于时间
当且仅当修改列表时两个线程在时间上发生重叠时,才会出现异常
否则,您不会得到异常
使用Thread.sleep()
无法可靠地强制两个线程重叠,因为内核总是可以在线程唤醒后决定任意调度线程
更新:OP想知道是否必须发生以下两种情况之一:
- 所有三项都已打印
- 其中一些已打印,并引发异常
更一般地说,查找
ConcurrentModificationException
不是检测多个线程同时修改和读取对象的可靠方法。事实上,异常的解决方案非常明确地解决了这一点
请注意,通常不能保证快速失效行为
说起来,在有人在场的情况下,不可能做出任何硬性保证
未同步的并发修改。快速失败操作抛出
ConcurrentModificationException尽最大努力。因此,
编写依赖此异常的程序是错误的
其正确性:ConcurrentModificationException仅应使用
检测错误
您试图复制的行为高度依赖于时间 当且仅当修改列表时两个线程在时间上发生重叠时,才会出现异常 否则,您不会得到异常 使用
Thread.sleep()
无法可靠地强制两个线程重叠,因为内核总是可以在线程唤醒后决定任意调度线程
更新:OP想知道是否必须发生以下两种情况之一:
- 所有三项都已打印
- 其中一些已打印,并引发异常
更一般地说,查找
ConcurrentModificationException
不是检测多个线程同时修改和读取对象的可靠方法。事实上,异常的解决方案非常明确地解决了这一点
请注意,通常不能保证快速失效行为
说起来,在有人在场的情况下,不可能做出任何硬性保证
未同步的并发修改。快速失败操作抛出
ConcurrentModificationException尽最大努力。因此,
编写依赖此异常的程序是错误的
其正确性:ConcurrentModificationException仅应使用
检测错误
从根本上说,您在一个线程中修改列表,而在另一个线程中对其进行迭代,而您使用的列表实现不支持这一点
ArrayList
迭代器实现似乎只在调用next()
时检测到无效修改,而在调用hasNext()
时检测不到。因此,如果在调用remove()
之前进入循环的最后一次迭代,则不会出现异常-hasNext()
只会返回false
。另一方面,如果remove()
发生在最后一次调用next()
之前(如果在另一个线程上注意到这一点-内存模型在这里起作用),那么您将得到异常。例如,如果您将循环内睡眠更改为Thread.sleep(2500)
,那么您将在第二次迭代开始时获得异常,因为remove()
调用将在它之前发生
如果您希望在多个线程中使用列表,并且其中至少有一个线程正在修改它,那么您应该使用支持该功能的实现,例如
copyonwritearlaylist
,基本上,您在一个线程中修改列表,而在另一个线程中对其进行迭代,您正在使用的列表实现不支持这一点
ArrayList
迭代器实现似乎只在调用next()
时检测到无效修改,而在调用hasNe时检测不到