並發場景下list的空指針異常和size大小問題


問題描述:

對一個源list使用並行流對其進行遍歷的時候往宿list添加元素,再次遍歷宿list的時候會拋出空指針異常問題而且會現宿list size大小也有問題。

問題復原:

@org.junit.Test
public void test2() {
    List<Integer> source = new LinkedList<>();
    List<String> target = new LinkedList<>();

    for (int i = 0; i < 1000; i++) {
        source.add(i);
    }

    source.parallelStream().forEach(i -> {
        target.add(String.valueOf(i));
    });

    System.out.println("target size -> " + target.size());

    for (String s : target) {
        System.out.println(s);
    }

}

運行結果

image-20211106090035966

image-20211106090052629

問題分析:

不開啟多線程的時候targetList的size一定是1000的,而且不會出現空指針異常。

這兩個問題其實都和size++這句話有關

1. size大小為什么不是1000

分析源碼:

add是尾插,源碼如下

void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

模擬一個場景

  1. size現在的值為100
  2. 線程a拿到了size大小100,此時cpu讓出執行權給線程b
  3. 線程b拿到了size大小也為100
  4. 那么無論他們誰先加1,最后size的值會被101覆蓋兩次,導致size的大小不會是102

2. 再次遍歷target的時候為什么會報空指針異常問題

其實也和尾插法這段代碼有關

模擬場景:

  1. 設現在的size為100

  2. 線程a拿到了鏈表尾部元素last 之后讓出執行權給線程b

  3. 線程b也拿到了相同的last之后一直執行完了add操作,此時size = 101

  4. 線程a也執行了 l.next = newNode;覆蓋了線程b的在size = 101 位置上的值,之后也進行了size++的操作,size = 102

  5. 此時出現的問題就是size = 101 位置上的值被覆蓋了兩次,但是102位置上的值是null。

  6. 所以遍歷的時候會報空指針異常。

  7. 因為last = newNode;這一步的重復覆蓋,可以預測所有的null都會是鏈表末尾。

    image-20211106091345360

    3. 關於為什么foreach增強for循環為什么會報空指針的問題

    Linkedlist的foreach循環,是依賴Iterator的,LinkedList也有自己的迭代器,源碼如下

    image-20211106092302610

    可以看出hasnext的判斷並不是node.next != null 而是 nextIndex < size 所以即使null都在末尾也會在 next = next.next的時候報空指針異常。

    3. 問題解決

    List<String> target = Collections.synchronizedList(new LinkedList<>());
    

    讓需要進行add操作的list,轉換成線程安全的。

    關於ArrayList

    其實也是和size++有關,與LinkedList不同的是由於實現尾插的方式不一致,所以導致null可能在中間

    源碼如下:

    image-20211106093041930

    結果如下:

    image-20211106093006725


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM