[英]Java, adding elements in an array an concurrently looping through it
抱歉,这是一个愚蠢的问题。 但是有人可以解释我在这种情况下会发生什么吗?
List<Integer> scores = new Arraylist<>() ;
scores =
Collections.synchronizedList(scores)
public void add(int element) {
...
scores.add(element)
...
}
public String retrieve(int element) {
...
For (Integer e : scores)....
....
Return something
}
让我们假设这个类是单例,得分是全局的。 多线程可以同时添加和检索分数
在这种情况下,当启动for循环并同时添加线程(或从列表中删除元素)时,它将引发并发修改示例吗?
谢谢
考虑到您编写示例的方式,坏事将会发生。
您的retrieve()
方法没有在synchronized
块中循环,并且您的两个方法都直接访问scores
,而不是使用Collections.synchronizedList()
方法返回的List
。
如果您查看Collections.synchronizedList()
的API,您会注意到它说
为了保证串行访问,至关重要的是,对后备列表的所有访问都必须通过返回的列表来完成。
当用户遍历返回列表时,必须手动对其进行同步:
不遵循此建议可能导致不确定的行为。
因此,您可能会收到ConcurrentModificationException
,否则可能会发生其他奇怪的事情。
即使您所有的访问都是通过同步的List
,但是如果在另一个线程中迭代List
时修改List
,仍然可能最终抛出ConcurrentModificationException
。 这就是为什么Collections.synchronizedList()
文档坚持要求您手动将迭代包装在与返回的List
同步的块中的原因。
用于ConcurrentModificationException的API说
例如, 通常不允许一个线程修改Collection,而另一个线程对其进行迭代 。 通常,在这些情况下,迭代的结果是不确定的。 如果检测到此行为,则某些Iterator实现(包括JRE提供的所有通用集合实现的实现) 可能会选择抛出此异常 。 执行此操作的迭代器称为快速失败迭代器,因为它们会快速干净地失败,而不是在未来的不确定时间内冒任意,不确定的行为的风险。
您的add方法不需要更改,但是您的retrieve()
方法应类似于:
public String retrieve(int element) {
// stuff
synchronized (scores) { // prevent scores from being modified while iterating
for (Integer e : scores) {
// looping stuff
}
}
// more stuff
return something;
}
这是一个小示例程序,演示了安全访问与不安全访问的行为:
public class Scratch {
private List<Integer> scores = Collections.synchronizedList(new ArrayList<Integer>());
public static void main(String[] args) throws Exception {
final Scratch s = new Scratch();
s.scores.add(1);
s.scores.add(2);
s.scores.add(3);
// keep adding things to the list forever
new Thread(new Runnable() {
@Override
public void run() {
try {
int i=100;
while (true) {
Thread.sleep(100);
s.scores.add(i++);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
System.out.println("This will run fine");
s.safeLoop();
System.out.println("This will cause a ConcurrentModificationException");
s.unsafeLoop();
}
public void safeLoop() throws InterruptedException {
synchronized (scores) {
for (int i : scores) {
System.out.println("i="+i);
Thread.sleep(100);
}
}
}
public void unsafeLoop() throws InterruptedException {
for (int i : scores) {
System.out.println("i="+i);
Thread.sleep(100);
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.