簡體   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