Java面試題集錦(4):執行緒不安全之ArrayList、Set、Map
技術標籤:java
我們知道ArrayList是執行緒不安全的,請編碼寫一個不安全的案例並給出解決方案?
一、ArrayList案例
1. 故障現象
class Scratch {
public static void main(String[] args) {
//List<String> list = Arrays.asList("a","b","c");
//list.forEach(System.out::println);
List<String> list = new ArrayList<>();
for(int i=1;i<=3;i++){
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);
}, String.valueOf(i)).start();
}
//java.util.ConcurrentModificationException
//ArrayList 併發常見的異常
}
}
ArrayLIst的add方法沒有加鎖,所以有可能報併發異常java.util.ConcurrentModificationException ,這是ArrayList 併發常見的異常。
2. 導致原因
併發爭搶修改導致,一個執行緒正在寫,另一個執行緒來搶奪,導致資料不一致異常,併發修改異常。
3. 解決方案
① 改為new Vector<>();
List<String> list = new Vector<>();
vector是JDK1.0版本的List實現,add方法加synchronized,犧牲了併發性。
List<String> list = Collections.synchronizedList(new ArrayList<>());
注:Java中Collection和Collections的區別
Collection 是一個集合介面(集合類的一個頂級介面)它提供了對集合物件進行基本操作的通用介面方法。Collection介面在Java 類庫中有很多具體的實現,其直接繼承介面有List與Set。
Collections 則是集合類的一個工具類/幫助類,其中提供了一系列靜態方法,用於對集合中元素進行排序、搜尋以及執行緒安全等各種操作。
(具體見https://www.cnblogs.com/cathyqq/p/5279859.html)
③使用CopyOnWriteArrayList
List<String> list = new CopyOnWriteArrayList<>();
寫時複製:併發時,當執行緒1使用list時,先拷貝一份list,擴容1,寫完之後將原來的陣列引用修改為拷貝的陣列,並返回true。
Set案例
Set也是執行緒不安全的
1. 故障現象
class Scratch {
public static void main(String[] args) {
//List<String> list = Arrays.asList("a","b","c");
//list.forEach(System.out::println);
Set<String> set = new HashSet<>();
for(int i=1;i<=30;i++){
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(set);
}, String.valueOf(i)).start();
}
//java.util.ConcurrentModificationException
//ArrayList 併發常見的異常
}
}
Set也會報錯:ConcurrentModificationException。
2. 解決方法
①使用synchronizedSet
Set<String> set = Collections.synchronizedSet(new HashSet<>());
②使用CopyOnWriteArrayList
Set<String> set = new CopyOnWriteArraySet<>();
另注: HashSet的底層是HashMap。HashSet的key是HashMap的key,HashSet的value都等於PRESENT(Object的常量)。
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
而PRESENT是一個Object的常量。
private static final Object PRESENT = new Object();
Map案例
Map也是執行緒不安全的
1. 故障現象
class Scratch {
public static void main(String[] args) {
//List<String> list = Arrays.asList("a","b","c");
//list.forEach(System.out::println);
Map<String,String> map = new HashMap();
for(int i=1;i<=10;i++){
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));
System.out.println(map);
}, String.valueOf(i)).start();
}
//java.util.ConcurrentModificationException
//ArrayList 併發常見的異常
}
}
ArrayList、Set、Map丟擲的異常一樣,說明三者的底層是同一套體系。
2. 解決方法
①使用ConcurrentHashMap
Map<String,String> map = new ConcurrentHashMap();
①使用Collections.synchronizedMap
Map<String,String> map = new Collections.synchronizedMap(new HashMap<>());