ArrayList在多線程情況下,不安全 具體代碼 package com.shaonian.juc.list_thread_secure; import java.util.ArrayList; import java.util.List; import java.util.UUID; /** * ...
ArrayList在多線程情況下,不安全
具體代碼
package com.shaonian.juc.list_thread_secure;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* @author 長名06
* @version 1.0
* 演示ArrayList集合線程不安全
*/
public class ListDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
//會出現java.util.ConcurrentModificationException 併發修改異常
list.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
}
解決方案
使用Vector/ConcurrentHashMap集合
package com.shaonian.juc.list_thread_secure;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
/**
* @author 長名06
* @version 1.0
* Vector集合,線程安全 不推薦使用
*/
public class VectorDemo {
public static void main(String[] args) {
List<String> list = new Vector<>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
}
使用Collections中的synchronizedList()/synchronizedMap()
package com.shaonian.juc.list_thread_secure;
import java.util.*;
/**
* @author 長名06
* @version 1.0
* 使用Collections中的synchronizedList方法,轉成對應線程安全的集合,不推薦使用
*/
public class CollectionsDemo {
public static void main(String[] args) {
List<String> list = Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i < 30; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
}
使用CopyOnwriteArrayList
涉及到寫時複製技術,就是說,此集合是可以併發讀,但是寫操作,同時只能有一個線程執行。寫操作時,先將原有的集合內容,copy一份,然後寫入新的內容,然後再覆蓋原有的集合,覆蓋後的集合,就可以讀和寫了
package com.shaonian.juc.list_thread_secure;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author 長名06
* @version 1.0
* 使用線程安全的集合如CopyOnWriteArrayList
*/
public class CopyOnWriteArrayListDemo {
public static void main(String[] args) {
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
}
CopyOnWriteList
CopyOnWriteList相當於線程安全的ArrayList,和ArrayList一樣,它是個可變數組,不過有一下特性
- 1.它最適合具有以下特征的程式使用,List大小通常保持很小,讀操作遠多於寫操作,需要再遍歷期間防止線程間的衝突。
- 2.線程安全。
- 3.可變操作,需要複製整個數組,所以可變操作(add(),set(),remove()等)操作開銷大。
- 4.迭代器支持hasNext(),next()等不可變操作,但是不支持remove()等操作。
- 5.使用迭代器進行遍歷的速度會很快,並且不會和其他線程發生衝突,再構造迭代器時,迭代器依賴於不變的數組快照。
有數組拷貝的操作,會存在一個問題,數據不一致的問題,如果寫線程還沒來得及寫入記憶體,其他線程就會讀到臟數據。但是在CopyOnWriteList中,保存數據的數組,使用volatile關鍵字修飾,也就是說,當前數組的修改,會被其他線程立刻讀取到。保證了一個線程讀取volatile修飾的數組,總能看到其他線程對改數組最後的寫入,就這樣,通過volatile提供了“讀取數據總是最新的”這個機制的保證。
HashSet和HashMap在多線程情況下,不安全
package com.shaonian.juc.list_thread_secure;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author 長名06
* @version 1.0
*/
public class HashSetDemo {
public static void main(String[] args) {
//解決方案,使用CopyOnWriteHashSet
Set<String> set = new HashSet<>();
for (int i = 0; i < 30; i++) {
//會出現java.util.ConcurrentModificationException
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(set);
}, String.valueOf(i)).start();
}
}
}
package com.shaonian.juc.list_thread_secure;
import java.util.*;
/**
* @author 長名06
* @version 1.0
*/
public class HashMapDemo {
public static void main(String[] args) {
//解決方案,使用ConcurrentHashMap
Map<String,String> map = new HashMap<>();
for (int i = 0; i < 30; i++) {
String key = String.valueOf(i);
//會出現java.util.ConcurrentModificationException
new Thread(() -> {
map.put(key,UUID.randomUUID().toString().substring(0, 8));
System.out.println(map);
}, String.valueOf(i)).start();
}
}
}
小結
線程不安全與其對應的安全集合
集合中存線上程存在和線程不安全的例如ArrayList -- Vector HashMap -- HashTable,但是以上都是syncronized關鍵字實現的。
Collections構建的線程安全集合
通過使用Collections中的synchronizedList()/synchronizedMap(),將不安全的集合,轉成線程安全的。
java.util.concurrent包下的
CopyOnWriteList和CopyOnWriteSet類型,通過動態數組與加鎖保證線程安全。
只是為了記錄自己的學習歷程,且本人水平有限,不對之處,請指正。下·