Java 为什么此代码没有';当多个线程使用迭代器同时在同一arraylist上工作时,不会抛出ConcurrentModificationException
这里有两个线程在同一个arraylist上工作,一个线程读取元素,另一个线程删除特定元素。我希望这会引发Java 为什么此代码没有';当多个线程使用迭代器同时在同一arraylist上工作时,不会抛出ConcurrentModificationException,java,iterator,concurrentmodification,Java,Iterator,Concurrentmodification,这里有两个线程在同一个arraylist上工作,一个线程读取元素,另一个线程删除特定元素。我希望这会引发ConcurrentModificationException。但它不扔为什么 import java.util.ArrayList; import java.util.ConcurrentModificationException; import java.util.Iterator; public class IteratorStudies { public static f
ConcurrentModificationException
。但它不扔为什么
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
public class IteratorStudies {
public static final ArrayList<String> arr ;
static{
arr = new ArrayList<>();
for(int i=0;i<100;i++) {
arr.add("someCommonValue");
}
arr.add("someSpecialValue");
}
private static Integer initialValue = 4;
public static void main(String x[]) {
Thread t1 = new Thread(){
@Override
public void start(){
Iterator<String> arrIter = arr.iterator();
while(arrIter.hasNext()){
try {
String str = arrIter.next();
System.out.println("value :" + str);
}catch(ConcurrentModificationException e){
e.printStackTrace();
}
}
System.out.println("t1 complete:"+arr);
}
};
Thread t2 = new Thread(){
@Override
public void start(){
Iterator<String> arrIter = arr.iterator();
while(arrIter.hasNext()){
String str = arrIter.next();
if(str.equals("someSpecialValue")){
arrIter.remove();
}
}
System.out.println("t2 complete:"+arr);
}
};
t2.start();
t1.start();
}
}
import java.util.ArrayList;
导入java.util.ConcurrentModificationException;
导入java.util.Iterator;
公共类迭代器研究{
公共静态最终数组列表arr;
静止的{
arr=新的ArrayList();
对于(int i=0;i您已经覆盖了两个线程实例的start
方法,而不是run
,并且这些方法在主执行线程中完成,因此,不会同时执行线程,也不会在此处发生ConcurrentModificationThreadException
。<凯斯
ConcurrentModificationException与并发无关
您可能会认为,给定名称,CoModEx是关于并发性的。事实并非如此。如中所示,您不需要线程来获取它。在这里,这个简单的代码将抛出它:
void example() {
var list = new ArrayList<String>();
list.add("a");
list.add("b");
for (String elem : list) {
if (elem.equals("a")) list.remove(elem);
}
}
void示例(){
var list=new ArrayList();
列表。添加(“a”);
列表。添加(“b”);
for(字符串元素:列表){
如果(elem.equals(“a”))列表删除(elem);
}
}
这是因为EcoModex是由迭代器抛出的,这意味着发生了这种情况:
有人做了一个迭代器
有人以某种方式更改了列表(而不是通过迭代器的.remove()方法)
有人在#1中生成的迭代器上运行任何相关方法
因此,在上面的例子中,foreach循环隐式地生成一个迭代器(#1),然后调用list.remove
方法(#2),然后再次点击foreach循环,我们调用该迭代器上的相关方法(.hasNext()
),瞧,出现了CoModEx
事实上,多线程的可能性较小:毕竟,您应该假设如果您与来自多个线程的某个对象交互,该对象已损坏,因为该行为是未指定的,因此,您有一个bug,更糟糕的是,很难对其进行测试。如果您从另一个线程修改纯jane arraylist在迭代过程中,不能保证您有一个CoModEx。您可能会得到它。您可能不会。计算机可能会离开办公桌,在百老汇碰碰运气。“未指定的行为”是一个很好的表达方式:“不要,说真的。这会一直伤害到你,因为你无法测试它;这会在你开发它的整个过程中都很好地工作,而且当你向大假发客户提供重要的演示时,它会以令人尴尬的方式在你身上失败。”
从多个线程与一个对象交互的方法非常仔细:检查特定对象的文档,明确说明发生了什么(即使用java.util.concurrent
包中的内容,该包专门设计为“从多个线程与之交互”用例),如果做不到这一点,请使用锁定。这些都是棘手的事情,因此在java中执行多线程的通常方法是首先不使用共享状态。尽可能多地隔离,反转控制,并使用具有内置事务本质的消息传递策略,如消息队列(rabbitmq和friends)和数据库(有交易的)
如何使用线程
您可以重写run()
方法,然后通过调用start
方法来启动线程。或者更好的方法是,不要重写run,在创建线程实例时传递一个可运行的实例
这就是你使用线程的方式。你没有-你覆盖了start,这意味着启动这些线程根本不会生成一个新线程,它只是在你的线程中运行负载。这解释了你的具体情况,但你正在尝试做什么(通过弄乱另一个线程的列表来见证CoModEx)也不会给你一个CoModEx——它会给你一个未指明的行为,这意味着任何事情都会发生