繁体   English   中英

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM