首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 並發編程實戰:同步容器的陷阱與隱藏迭代器問題

     編輯:Java 綜合教學

Java 並發編程實戰:同步容器的陷阱與隱藏迭代器問題

同步容器(Vector、Hashtable、Collections.synchronizedXxx 包裝類)雖然單個方法是線程安全的,但在復合操作場景下仍存在競態條件風險。本文深入剖析「檢查-然後執行」模式的併發缺陷,以及隱藏迭代器導致的 ConcurrentModificationException 異常。

問題一:復合操作非原子性

以 Vector 為例,「獲取最後一個元素」與「刪除最後一個元素」是兩個獨立的同步方法。若線程 A 正在 get(lastIndex),線程 B 同時 remove(lastIndex),可能導致 A 拋出 ArrayIndexOutOfBoundsException。

錯誤示例

// 線程 A
Object last = vector.get(vector.size() - 1);

// 線程 B
vector.remove(vector.size() - 1); // 可能導致線程 A 索引越界

正確做法:客戶端加鎖

synchronized (vector) {
    Object last = vector.get(vector.size() - 1);
    // 或刪除操作
    vector.remove(vector.size() - 1);
}

將復合操作封裝在同一把鎖(容器對象自身)中,使其成為原子操作。

問題二:隱藏迭代器導致的 CME 異常

看似線程安全的類內部若在 toString()、hashCode()、equals()、日誌輸出等場景隱式調用迭代器,而在迭代過程中其他線程修改了容器,即拋出 ConcurrentModificationException。

典型隱藏迭代器場景

public class HiddenIterator {
    private final Set<Integer> set = new HashSet<>();

    public synchronized void add(Integer i) { set.add(i); }
    public synchronized void remove(Integer i) { set.remove(i); }

    public void addTenThings() {
        Random r = new Random();
        for (int i = 0; i < 10; i++) add(r.nextInt());
        // 危險!toString() 內部會迭代 set
        // 若此時其他線程調用 add/remove,拋出 CME
        System.out.println("DEBUG: added ten elements to " + set);
    }
}

修復方案

public void addTenThings() {
    Random r = new Random();
    for (int i = 0; i < 10; i++) add(r.nextInt());
    synchronized (this) { // 持有對象鎖,防止併發修改
        System.out.println("DEBUG: added ten elements to " + set);
    }
}

最佳實踐總結

  1. 識別復合操作:凡涉及「先檢查/讀取,後修改/寫入」的多步操作,必須客戶端加鎖。
  2. 警惕隱式迭代:日誌輸出、調試打印、JSON 序列化、集合轉數組等都可能觸發迭代。
  3. 優先使用並發容器:JUC 包下的 ConcurrentHashMap、CopyOnWriteArrayList、ConcurrentLinkedQueue 等提供更高併發性能與弱一致性迭代器,避免 CME。
  4. 不可變集合:Java 9+ List.of()、Set.of()、Guava ImmutableList 等天然線程安全,適合只讀場景。

核心結論

同步容器僅保證單方法原子性,復合操作與隱式迭代是兩大併發陷阱。實戰中應優先選用 java.util.concurrent 並發容器,或顯式加鎖保護臨界區。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved