java集合安全

java / 2022-08-22
0 1,436

一、ArrayList集合

1、现象
    /**
     * 1、可能会抛出
     *   java.util.ConcurrentModificationException(并发修改异常)
     * 2、导致原因
     *
     * 3、解决方案
     * @param
     */
    public static void arrayList_notSafe(){
        List<String> list = new ArrayList<>();
        // 解决方案一:Vector 天生加锁
//        List<String> list = new Vector<>();
        // 解决方案二:使用 Collections.synchronizedList 完成集合的同步
//        List<String> list = Collections.synchronizedList(new ArrayList<>());
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString());
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }

// java.util.ConcurrentModificationException
2、原因
    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

ArrayList的add方法并不能保证线程安全。

3、解决方案
  • 1⃣️ 加锁

可以通过加锁来实现代码的同步,而JDK中本身就带有这样的实现。那就是Vector集合。

  • 2⃣️ 集合辅助类

JDK带了一个集合辅助类Collections,里面提供了对不安全集合的安全包装,而ArrayList集合就可以用里面的synchronizedList进行包装。

  • 3⃣️ 写时复制

使用CopyOnWriteArrayList类。底层使用 ReentrantLock 完成集合的写时复制。会把集合拷贝一份,对副本进行操作,不会影响原集合的读,操作结束后将原集合替换掉,这样的好处就是可以对容器进行并发读,而不需要加锁。算是一种读写分离。

二、set集合

1、现象
    public static void set_notSafe(){
        Set set = new HashSet<>();
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                set.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
    }

// java.util.ConcurrentModificationException
2、原因

在运行上面这段代码的时候,大概率会遇到一个错误java.util.ConcurrentModificationException,并发修改异常。由于HashSet底层实现是HahsMap,在add的时候调用的是HashMapput方法,所以在多线程的环境下抛出了异常。

3、解决方案
  • 1⃣️ 加锁

可以通过加锁来实现代码的同步。

  • 2⃣️ 集合辅助类

JDK带了一个集合辅助类Collections,里面提供了对不安全集合的安全包装,而set集合就可以用里面的synchronizedSet进行包装。

  • 3⃣️ 写时复制

使用CopyOnWriteArraySet类。这个类Set集合的实现类,底层是CopyOnWriteArrayList,上面说到CopyOnWriteArrayList通过写时复制来保证集合安全。

三、Map集合

Map集合跟Set集合的情况相似。JDK中提供了一个ConcurrentHashMap类保证集合安全。