多线程操作ArrayList不安全解决方案
多線程操作ArrayList不安全解決方案
- 1. 需求概述
- 2. 需求驗證
- 2.1 測試方案
- 2.2 驗證結論
- 2.3 代碼實現
- 3. 解決方案
- 3.1 Vector
- 3.1 Collections工具類
- 3.1 CopyOnWriteArrayList
1. 需求概述
我們都知道List,ArrayList,以及Map,HashMap等常用的集合都是線程不安全的,替代及解決方案都是對應的current包下的對應的結合容器,比如hashMap對應CurrentHashMap,ArrayList對應什么呢,vector,也可以,就是效率太低,首選CopyOnWriteArrayList,讀寫分離,可重入鎖效率更高。
以下就是測試驗證以及原理分析。
2. 需求驗證
2.1 測試方案
測試案例采用兩種方式ArrayList與CopyOnWriteArrayList作為集合容器,同時開啟多線程分別往兩個容器內插入數據。
2.2 驗證結論
最終結果是ArrayList數據丟失,而CopyOnWriteArrayList數據正常。
2.3 代碼實現
package com.zrj.unit.collection;import com.google.common.util.concurrent.ThreadFactoryBuilder; import lombok.SneakyThrows;import java.util.ArrayList; import java.util.List; import java.util.concurrent.*;/*** 多線程操作ArrayList** @author zrj* @since 2021/12/17**/ public class ThreadArrayList {// 自定義線程名稱private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("thread-%d").build();// 定義線程池private static ExecutorService pool = new ThreadPoolExecutor(30, 200, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());/*** 多線程下ArrayList類線程不安全的解決方法及原理* 測試驗證* 【simpleList】休眠30秒等待線程執行完成,理論長度6000,實際長度:5426* 【simpleCopyOnWriteList】休眠30秒等待線程執行完成,理論長度6000,實際長度:6000* 解決方案* 1.首選CopyOnWriteArrayList,讀寫分離,可重入鎖。* 2.其次vector,synchronized效率低下,逐漸被遺棄。*/@SneakyThrowspublic static void main(String[] args) {//驗證ArrayListList<String> simpleList = new ArrayList<>(100);//啟動30個線程for (int i = 0; i < 30; i++) {pool.execute(() -> {//每個線程插入100條數據for (int j = 0; j < 100; j++) {simpleList.add("simpleList");}});}for (int i = 0; i < 30; i++) {pool.execute(() -> {//每個線程插入100條數據for (int j = 0; j < 100; j++) {simpleList.add("simpleList");}});}Thread.sleep(20000);System.out.println("【simpleList】休眠20秒等待線程執行完成,理論長度6000,實際長度:" + simpleList.size());//驗證CopyOnWriteArrayListCopyOnWriteArrayList<String> simpleCopyOnWriteList = new CopyOnWriteArrayList<>();//啟動30個線程for (int i = 0; i < 30; i++) {pool.execute(() -> {//每個線程插入100條數據for (int j = 0; j < 100; j++) {simpleCopyOnWriteList.add("simpleList");}});}for (int i = 0; i < 30; i++) {pool.execute(() -> {//每個線程插入100條數據for (int j = 0; j < 100; j++) {simpleCopyOnWriteList.add("simpleList");}});}Thread.sleep(20000);System.out.println("【simpleCopyOnWriteList】休眠20秒等待線程執行完成,理論長度6000,實際長度:" + simpleCopyOnWriteList.size());} }3. 解決方案
3.1 Vector
Vector類 是可以實現自動增長的對象數組,其add操作是用synchronized關鍵字修飾的,從而保證了add方法的線程安全。保證了數據的一致性,但由于加鎖導致訪問性能大大降低。
vector源碼分析
3.1 Collections工具類
用Collections工具類將線程不安全的ArrayList類轉換為線程安全的集合類。小體量數據的ArrayList類可以使用這種方法創建線程安全的類。
List<String> list=Collections.synchronizedList(new ArrayList);3.1 CopyOnWriteArrayList
CopyOnWrite容器即寫時復制的容器。通俗的理解是當我們往一個容器添加元素的時候,不直接往當前容器添加,而是先將當前容器進行Copy,復制出一個新的容器,然后新的容器里添加元素,添加完元素之后,再將原容器的引用指向新的容器。這樣做的好處是我們可以對CopyOnWrite容器進行并發的讀,而不需要加鎖,因為當前容器不會添加任何元素。所以CopyOnWrite容器也是一種讀寫分離的思想,讀和寫不同的容器。
public boolean add(E e) {//1、先加鎖final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;//2、拷貝數組Object[] newElements = Arrays.copyOf(elements, len + 1);//3、將元素加入到新數組中newElements[len] = e;//4、將array引用指向到新數組setArray(newElements);return true;} finally {//5、解鎖lock.unlock();} }總結
以上是生活随笔為你收集整理的多线程操作ArrayList不安全解决方案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mac | 好用的软件推荐
- 下一篇: 使用OPCServer通过Modbus协