对不起,如果这是一个愚蠢的问题。但有人可以解释一下这样的场景会发生什么吗?
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
}
让我们假设这个类是一个singelton,并且得分是全局的。多个线程可以同时添加和检索分数
在这种情况下,当启动for循环并且同时一个线程正在添加(或从列表中删除一个元素)时,它会抛出一个并发的修改例程吗?
谢谢
答案 0 :(得分:2)
考虑到你写下你的榜样,不好的事情会发生。
您的retrieve()
方法在synchronized
块中没有循环,并且两个的方法都直接访问scores
,而不是使用List
方法返回的Collections.synchronizedList()
。
如果你看一下Collections.synchronizedList()
的API,你会发现它说的是
为了保证串行访问,必须通过返回的列表完成对后备列表的所有访问。
当迭代时,用户必须手动同步返回的列表:
如果不遵循此建议,可能会导致行为不确定。
所以可能会获得ConcurrentModificationException
,或者其他可能发生的奇怪事件。
即使您通过同步List
进行了所有访问,如果您在另一个帖子中迭代ConcurrentModificationException
时修改了List
,您仍然可以获得Collections.synchronizedList()
。这就是为什么List
文档坚持要求您手动将迭代包装在其返回的retrieve()
上同步的块中的原因。
ConcurrentModificationException的API说
例如,一个线程通常不允许修改Collection而另一个线程正在迭代它。通常,在这些情况下,迭代的结果是不确定的。一些Iterator实现(包括JRE提供的所有通用集合实现的实现)可以选择在检测到此行为时抛出此异常。执行此操作的迭代器称为失败快速迭代器,因为它们快速而干净地失败,而不是在未来的未确定时间冒着任意的,非确定性行为的风险。
您的添加方法不需要更改,但您的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);
}
}
}
这是一个小型示例程序,用于演示安全与不安全访问的行为:
{{1}}