Java,在数组中添加元素并同时循环遍历它

时间:2015-04-10 18:38:04

标签: java concurrency iteration

对不起,如果这是一个愚蠢的问题。但有人可以解释一下这样的场景会发生什么吗?

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循环并且同时一个线程正在添加(或从列表中删除一个元素)时,它会抛出一个并发的修改例程吗?

谢谢

1 个答案:

答案 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}}