一、ConcurrentHashMap 的细粒度锁机制

1.1 JDK 1.8 后的实现原理

· 放弃分段锁:改用 synchronized + CAS 操作
· 数据结构:数组 + 链表 + 红黑树
· 锁粒度:只锁住单个桶(bucket)的头节点

```java
// 核心方法 putVal 简化逻辑
final V putVal(K key, V value, boolean onlyIfAbsent) {
    int hash = spread(key.hashCode());
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    
    if ((tab = table) == null || (n = tab.length) == 0)
        tab = initTable();  // 无锁初始化
    
    if ((p = tabAt(tab, i = (n - 1) & hash)) == null) {
        if (casTabAt(tab, i, null, new Node<>(hash, key, value)))
            break;  // CAS 插入成功
    } else {
        synchronized (p) {  // 仅锁当前桶
            // 链表或红黑树插入
        }
    }
}
```

1.2 与 Hashtable 对比

特性 Hashtable ConcurrentHashMap
锁机制 全表锁 (synchronized method) 桶级锁
并发读 阻塞 无锁 (volatile 读)
迭代器 fail-fast weakly consistent

---

二、CopyOnWriteArrayList 写时复制

2.1 适用场景

· 读多写极少:如黑名单、配置列表
· 迭代操作远多于修改

2.2 实现要点

```java
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();
    }
}
```

代价:每次修改都复制整个数组,内存开销大

---

三、内存可见性三要素

3.1 volatile 的 happens-before 规则

· 写 volatile → 读 volatile 建立 happens-before
· 不保证原子性(count++ 非原子)

3.2 错误示例与修正

```java
// ❌ 可能死循环(看不到修改)
class StopThread {
    private static boolean stop;
    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            while (!stop) {}  // 可能永远运行
        }).start();
        Thread.sleep(1000);
        stop = true;
    }
}

// ✅ 正确方式
private static volatile boolean stop;
```

3.3 synchronized 的可见性保证

· 线程释放锁前,强制将本地内存刷新到主内存
· 线程获取锁时,重新加载主内存数据

---

四、抽象队列同步器(AQS)基础

4.1 核心设计

· state:volatile int 状态标志
· CLH 队列:FIFO 双向链表管理等待线程

4.2 独占锁获取流程

```java
// tryAcquire 需子类实现
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
```

关键点:tryAcquire 需 CAS 修改 state

---

五、实战陷阱:ConcurrentHashMap 复合操作

```java
// ❌ 非原子操作
if (!map.containsKey(key)) {
    map.put(key, value);  // 两次操作间可能被其他线程插入
}

// ✅ 原子替代
map.putIfAbsent(key, value);

// ❌ 错误删除
map.remove(key);
// ✅ 条件删除
map.remove(key, expectedValue);
```

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐