JAVA集合框架中線程安全問題


1、ArraryList相關

ArrayList是線程不安全的,在多線程下同時操作一個集合會出java.util.ConcurrentModificationException異常(並發修改異常),如下所示:

    public static void main(String[] args) throws IOException {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, "thread" + i).start();
        }
    }

  解決辦法:① 、使用List<String> list = new Vector<>();

       ②、 使用List<String> list = Collections.synchronizedList(new ArrayList<>());

       ③、 使用List<String> list = new CopyOnWriteArrayList<>();

Vector線程安全原因:

    /**
     * Appends the specified element to the end of this Vector.
     *
     * @param e element to be appended to this Vector
     * @return {@code true} (as specified by {@link Collection#add})
     * @since 1.2
     */
    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }

CopyOnWriteArrayList線程安全原因:

/**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

ReentrantLock和synchronized 區別:https://baijiahao.baidu.com/s?id=1648624077736116382&wfr=spider&for=pc

 ArrayList生僻知識點:

①、創建時可以自己指定長度,不指定默認長度為10,每次擴容以當前長度0.5倍進行擴容,第一次10,第二次15

②、ArrayList底層是數組結構,所以不能通過forEache和增強for循環去進行刪除(迭代器方式)和新增

③、可以通過正常for循環去操作for(int i=0;i<list.size();i++),雖然此方法不報錯,但是不推薦(涉及到數組下標移動問題)

④、可通過臨時集合將待刪除數據記錄,待循環結束后,使用removeAll()一起刪除

提示:Vectore初始默認長度也為10,但是每次擴容都是按1倍進行擴容,第一次10,第二次20

2、HashSet相關

 HashSet為線程不安全的,在多線程下同時操作一個集合也會出java.util.ConcurrentModificationException異常(並發修改異常),實例如下:

    public static void main(String[] args) throws IOException {
        Set<String> list = new HashSet<>();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, "thread" + i).start();
        }
    }

 

解決辦法:

①、使用Set<String> list = Collections.synchronizedSet(new HashSet<>());

②、使用Set<String> list = new CopyOnWriteArraySet<>();

CopyOnWriteArraySet的底層依舊是采用和CopyOnWriteArrayList,如下所示:

     /**
     * Creates an empty set
     */
    public CopyOnWriteArraySet() {
        al = new CopyOnWriteArrayList<E>();
    }

 

HashSet生僻知識點:

①、HashSet底層是通過HashMap進行實現的,故此HashMap也會有線程安全問題

②、HashSet的add方法底層就是調用的HashMap的put方法,只不過value全部都是一個常量對象private static final Object PRESENT = new Object();

③、HashSet的元素全部都是作為HashMap的key值,所以自定義HashSet元素相等方法時必須要重寫HashCode方法

3、HashMap相關

HashMap跟HashSet一樣,因為HashSet底層就是采用HashMap,所以也會有線程安全的問題

解決辦法:

①、使用Map<String, Object> objectObjectMap = Collections.synchronizedMap(new HashMap<>());

②、使用Map<Object, Object> objectObjectConcurrentHashMap = new ConcurrentHashMap<>();

③、使用Map<Object, Object> hashtable = new Hashtable<>();

Hashtable和Vector相似,都是jdk1.0的產物,都是采用了synchronized關鍵字進行枷鎖,所以都是線程安全,但是效率稍微低一點

ConcurrentHashMap則是采用對鏈表進行分段加鎖實現現在安全,效率比HashTable高很多,推薦使用

HashMap生僻知識點:

①、HashMap底層是數組+鏈表結構實現

②、創建一個HashMap是默認數組長度為16(必須是2的冪),每次擴容時以之前的2倍進行擴容,第一次16,第二次48

③、HashMap的初始容量可以自己指定,一版要求都是以2的n次冪的形式存在的

結論:

HashMap計算添加元素的位置時,使用的位運算,這是特別高效的運算;另外,HashMap的初始容量是2的n次冪,擴容也是2倍的形式進行擴容,是因為容量是2的n次冪,可以使得添加的元素均勻分布在HashMap中的數組上,減少hash碰撞,避免形成鏈表的結構,使得查詢效率降低!

為什么HashMap的容量是2的n次冪?

 HashMap的容量為什么是2的n次冪,和這個(n - 1) & hash的計算方法有着千絲萬縷的關系,符號&是按位與的計算,這是位運算,計算機能直接運算,特別高效,按位與&的計算方法是,只有當對應位置的數據都為1時,運算結果也為1,當HashMap的容量是2的n次冪時,(n-1)的2進制也就是1111111***111這樣形式的,這樣與添加元素的hash值進行位運算時,能夠充分的散列,使得添加的元素均勻分布在HashMap的每個位置上,減少hash碰撞


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM