Java集合 安全 問題

集合 安全 問題

  1. 集合不安全現象

    • ArrayList、HashSet、HashMap在迭代的時候如果同時對其進行修改就會、拋出java.util.ConcurrentModificationException異常(併發修改異常
    List<String> list = new ArrayList<>();//線程不安全
    Set<String> set = new HashSet<>();//線程不安全
    Map<String,String> map = new HashMap<>();//線程不安全
    
    for (int i = 0; i <30 ; i++) {
                new Thread(()->{
                    list.add(UUID.randomUUID().toString().substring(0,8));
                    System.out.println(list);
                },String.valueOf(i)).start();
            }
    
    • ArrayList原因分析
      ArrayList的源碼add()方法中沒有synchronized、線程不安全

      public boolean add(E e) {
          ensureCapacityInternal(size + 1);  // Increments modCount!!
          elementData[size++] = e;
          return true;
      }
      
  2. 解決方案

    1. 換成線程安全的Vector()方法、Vector的源碼中add()方法有synchronized、線程安全

      //List<String> list = new Vector<>();
      
      public synchronized boolean add(E e) {
          modCount++;
          ensureCapacityHelper(elementCount + 1);
          elementData[elementCount++] = e;
          return true;
      }
      
    2. 利用Collections提供了方法**Collections.synchronizedList()**保證list是同步線程安全的

      List<String> list = Collections.synchronizedList(new ArrayList<>());
      
      for (int i = 0; i <30 ; i++) {
                  new Thread(()->{
                      list.add(UUID.randomUUID().toString().substring(0,8));
                      System.out.println(list);
                  },String.valueOf(i)).start();
              }
      
    3. 使用寫時複製、**CopyOnWriteArrayList()**是Arraylist的一種線程安全變體,其中所有可變操作(add、set等)都是通過生成底層Object[ ]數組的新副本來實現的。

      //List<String> list = new CopyOnWriteArrayList<>();
      
      //底層源碼
      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();
          }
      }
      
      • CopyOnWrite容器即寫時複製的容器。往一個容器添加元素的時候,不直接往當前容器Object[]添加,而是先將當前容器Object[]進行Copy,複製出一個新的容器Object[] newElements,然後向新的容器Object[] newElements裏添加元素。
        添加元素後,再將原容器的引用指向新的容器setArray(newElements)。
        這樣做的好處是可以對CopyOnWrite容器進行併發的讀,而不需要加鎖,因爲當前容器不會添加任何元素。
        所以CopyOnWrite容器也是一種讀寫分離的思想,讀和寫使用不同的容器。
  3. 與ArrayList相類比,HashSet和HashMap也有寫時複製:

    Set<String> set = new HashSet<>();//線程不安全
    Set<String> set = new CopyOnWriteArraySet<>();//線程安全
    
    Map<String,String> map = new HashMap<>();//線程不安全
    Map<String,String> map = new ConcurrentHashMap<>();//線程安全
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章