Java ArrayList線程安全問題

今天在學習集合Collection,裏面講到了List下面的實現類ArrayList、LinkedArrayList和Vector的線程安全問題。

先拋出結論:

    ArrayList和LinkedList是線程不安全的,Vector是線程安全的。

分析:

    線程的安全性是對於多線程來說的,如果是單線程的程序,可以不用考慮安全問題。

    以ArrayList和Vector的add方法爲例,先看源碼。

    

    可以看到,Vecrot使用了synchronized關鍵字(不知道什麼意思的話,自行百度),所以它是安全的,ArrayList就是不安全的。爲什麼會這麼說,原因如下:

用elementData[size++] = e 這一步作爲例子來講。
從這兒可以看出,這步操作也不是一個原子操作,它由如下兩步操作構成:
elementData[size] = e;
size = size + 1;
在單線程執行這兩條代碼時沒有任何問題,但是當多線程環境下執行時,可能就會發生一個線程的值覆蓋另一個線程添加的值,具體邏輯如下:

列表大小爲0,即size=0
線程A開始添加一個元素,值爲A。此時它執行第一條操作,將A放在了elementData下標爲0的位置上。
接着線程B剛好也要開始添加一個值爲B的元素,且走到了第一步操作。此時線程B獲取到size的值依然爲0,於是它將B也放在了elementData下標爲0的位置上。
線程A開始將size的值增加爲1
線程B開始將size的值增加爲2
這樣線程AB執行完畢後,理想中情況爲size爲2,elementData下標0的位置爲A,下標1的位置爲B。而實際情況變成了size爲2,elementData下標爲0的位置變成
了B,下標1的位置上什麼都沒有。並且後續除非使用set方法修改此位置的值,否則將一直爲null,因爲size爲2,添加元素時會從下標爲2的位置上開始。

    

寫了一段代碼測試下

 /**
     * 測試線程安全性
     */
    @Test
    public void test3() {
        List<Integer> list = new ArrayList<>();

        new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    list.add(i);
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                for (int i = 1000; i < 5000; i++) {
                    list.add(i);
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 打印所有結果
        for (int i = 0; i < list.size(); i++) {
            System.out.println("第" + (i + 1) + "個元素爲:" + list.get(i));
        }
    }

多跑幾次,就會發現,會有元素爲null的打印出來,我測試的是第11個元素丟了,本來應該是5的。

如果想避免這種問題,可以再add方法那裏加上synchronized,對list做下同步就好了。代碼如下:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章