簡體   English   中英

當list.remove同時在2個線程中運行時(同時),IndexOutOfBoundsException

[英]IndexOutOfBoundsException when list.remove run in 2 thread the same time(concurrently)

我在main方法中有一個列表,我想編寫兩個使用此列表的線程。 有時我在同步塊中捕獲IndexOutOfBoundsException(當線程調用remove方法時)。

主要方法:

public class PC {
    public static void main(String[] args) {
        List<String> strings = new ArrayList<>();
        new Costumer("c1", strings).start();
        new Costumer("c2", strings).start();
        new Producer("p1", strings).start();
        new Producer("p2", strings).start();
        new Producer("p3", strings).start();
        new Producer("p4", strings).start();
    }
}

服裝課:

class Costumer extends Thread {

    List<String> strings;
    public Costumer(String n, List<String> strings) {
        super(n);
        this.strings = strings;
    }
    @Override
    public void run() {
        while (true) {
            synchronized (strings) {
                try {
                    if (strings.isEmpty()) {
                        strings.wait();
                    }
                    strings.remove(0); // <- where exception is thrown
                } catch (InterruptedException ex) {
                }
            }
        }
    }
}

生產者類別:

class Producer extends Thread {

    List<String> strings;

    public Producer(String n, List<String> strings) {
        super(n);
        this.strings = strings;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (strings) {
                strings.add(String.valueOf(Math.random() * 1000));
                if (strings.size() == 1) {
                    strings.notify();
                }
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
            }
        }
    }
}

堆棧跟蹤:

Exception in thread "c2" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
        at java.util.ArrayList.rangeCheck(Unknown Source)
        at java.util.ArrayList.remove(Unknown Source)
        at Costumer.run(PC.java:40)

代碼中的問題是Costumer類中的if測試,必須將其替換為while循環,否則您可能會遇到競爭條件問題。 實際上,假設我們有一個使用者正在等待被通知,我們有一個使用者正在等待對字符串的鎖定,而我們的生產者具有對字符串的鎖定,並添加了新的字符串並由於沒有更多的字符串而調用通知。 因此,一旦它釋放了鎖,就可以說等待鎖的使用者首先獲得了它(是的,不要忘記已經被通知的使用者仍然需要獲得鎖,而不必先獲得鎖),然后刪除一個String,然后第二個使用者(已由該使用者通知的使用者)將從strings.wait()開始,並在不檢查是否為空的情況下調用strings.remove(0) ,然后您將獲得IndexOutOfBoundsException

換句話說,代碼應為:

@Override
public void run() {
    while (true) {
        synchronized (strings) {
            try {
                while (strings.isEmpty()) {
                    strings.wait();
                }
                strings.remove(0);
            } catch (InterruptedException ex) {
            }
        }
    }
}

無論如何,將您的條件包裝到while循環中是一個好習慣,以避免像這樣的怪異錯誤。 例如,您可以在ArrayBlockingQueue之類的類中檢查其完成方式,所有條件都在while循環中檢查。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM