為什么Vector和Collections.SynchronizedList的get方法要加鎖呢嫁赏?
1. 線程不安全的ArrayList
為什么說ArrayList是線程不安全的:
- add()操作拋出數(shù)組越界異常;
- add()操作會丟失元素侵歇;
- set()操作去修改元素腺毫,get()操作去獲取元素時癣疟,可以讀到新值也可能讀到舊值,無法保證一致性潮酒。
源碼分析:
//存放list集合元素的數(shù)組,默認(rèn)容量10
transient Object[] elementData;
//list大小
private int size;
add()的源碼:
public boolean add(E e) {
//確定添加元素之后睛挚,集合的大小是否足夠,若不夠則會進行擴容
ensureCapacityInternal(size + 1); // Increments modCount!!
//插入元素
elementData[size++] = e;
return true;
}
場景1:多個線程都沒進行擴容急黎,但是執(zhí)行了elementData[size++] = e;
時扎狱,便會出現(xiàn)“數(shù)組越界異常”勃教;
場景2:因為size++本身就是非原子性的淤击,多個線程之間訪問沖突,這時候兩個線程可能對同一個位置賦值故源,就會出現(xiàn)“size小于期望值的結(jié)果”污抬;
2. Vector和Collections.SynchronizedList的get方法要加鎖呢?
get()操作時集合中的元素不能并發(fā)的被修改,否則就易出現(xiàn)數(shù)據(jù)問題印机。
- Vector和Collections.SynchronizedList的get方法加了synchronized后可以保證順序性與實時一致性矢腻,當(dāng)一個線程在讀取數(shù)據(jù)時,一定可以看到其他線程解鎖前寫入的全部數(shù)據(jù)射赛。
- 并且Vector和Collections.SynchronizedList的數(shù)組并沒有用volatile修飾多柑,如果不加鎖,也無法保證可見性楣责。
3. 線程安全的3種List集合
//方法上使用sync關(guān)鍵字(讀寫均加鎖)
Vector vector = new Vector();
//寫操作每一次均copy一個數(shù)組竣灌,讀操作不加鎖(寫加鎖性能低,讀不加鎖性能極高)
CopyOnWriteArrayList<Integer> r2 = new CopyOnWriteArrayList<>();
//使用sync代碼塊裝飾傳入List的讀寫操作(讀寫均加鎖)
List<String> r3 = Collections.synchronizedList(new ArrayList<>());
- Vector/Collections.synchronizedList:讀寫均加鎖腐魂,來實現(xiàn)線程安全帐偎;
- CopyOnWriteArrayList基于寫時復(fù)制技術(shù)實現(xiàn)的,讀操作無鎖(讀取快照)蛔屹,寫操作有鎖削樊,體現(xiàn)了讀寫分離的思想,但是無法提供實時一致性兔毒。
4. 并發(fā)安全的案例
下面給出一個案例漫贞,即容易出現(xiàn)并發(fā)問題的場景:
public class TestList {
private static final ExecutorService VIEW_EXECUTOR = new ThreadPoolExecutor(2,4,1000,
TimeUnit.SECONDS,new ArrayBlockingQueue<>(2));
/**
* 目前比較常用的構(gòu)建線程安全的List有三種方法:
* <p>
* 使用Vector容器
* 使用Collections的靜態(tài)方法synchronizedList(List< T> list)
* 采用CopyOnWriteArrayList容器
*/
public static void main(String[] args) {
//常用方式:使用線程池并發(fā)處理,填充結(jié)果
ArrayList<Object> res = new ArrayList<>();
CompletableFuture.runAsync(() -> {
//todo 邏輯處理
//線程不安全育叁,需要使用一個線程安全的List迅脐,這里推薦Collections.synchronizedList
res.add("success");
}, VIEW_EXECUTOR);
}
}
推薦閱讀
重學(xué)Java并發(fā)編程(寫時復(fù)制技術(shù)在CopyOnWriteArrayList中的應(yīng)用)