集合類安全問題

集合類安全問題

集合類在寫項目的過程中使用的十分頻繁,但其實常用的幾種集合類並不是線程安全的,在多線程的場景下使用可能會出現問題,用一個小case來模擬一下,本文采用ArrayList爲例,Map和Set原理類似。

Eg: 新建個字符串集合,遍歷,一邊往集合裏寫數據,一邊打印集合內數據。

public static void main(String[] args) {
	List<String> list = new ArrayList<>();

    for (int i = 0; i < 5; i++) {
        new Thread(() -> {
            list.add(UUID.randomUUID().toString());
            System.out.println(list);
        }, "A").start();
    }   
}

運行會報出如下錯誤:java.util.ConcurrentModificationException

java.util.ConcurrentModificationException
at java.util.ArrayListItr.checkForComodification(ArrayList.java:907)atjava.util.ArrayListItr.checkForComodification(ArrayList.java:907) at java.util.ArrayListItr.next(ArrayList.java:857)
at java.util.AbstractCollection.toString(AbstractCollection.java:461)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at app.noSafeDemo.lambda$0(noSafeDemo.java:20)
at java.lang.Thread.run(Thread.java:748)

解決方法
  1. 使用Vector

使用線程安全的Vector,代碼進行稍微修改。

public static void main(String[] args) {
	List<String> list = new Vector<>();
	
	for (int i = 0; i < 15; i++) {
	    new Thread(() -> {
	        list.add(UUID.randomUUID().toString());
	        System.out.println(list);
	    }, "A").start();
	}   
}

Vector爲什麼可以做到線程安全?通過Jdk源碼查看,可以看出,Vector的add方法使用了synchronized關鍵字修飾。
在這裏插入圖片描述

  1. Collections工具類

Collections工具類是Jdk提供的工具類,包含了多種多樣的接口幫助我們將集合包裝成線程安全的。

public static void main(String[] args) {
	List<String> list = Collections.synchronizedList(new ArrayList<>());
	
	for (int i = 0; i < 15; i++) {
	    new Thread(() -> {
	        list.add(UUID.randomUUID().toString());
	        System.out.println(list);
	    }, "A").start();
	}
}
  1. CopyOnWriteArrayList

CopyOnWriteArrayList是JUC中提供的接口,它的底層採用了寫時複製,讀寫分離的思想,在往集合容器中添加元素時,不會直接加入集合中的Object[],而是先將Object[]複製一份成Object[] newElements,向Object[] newElements添加,添加完成後將引用指向Object[] newElements。

我們通過源碼來具體看看是怎麼執行的。
在這裏插入圖片描述

public static void main(String[] args) {
	 List<String> list = new CopyOnWriteArrayList<>();
	
	 for (int i = 0; i < 15; i++) {
	     new Thread(() -> {
	         list.add(UUID.randomUUID().toString());
	         System.out.println(list);
	     }, "A").start();
	 }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章