Skip to content
第 23 章 后端 ⏱ 8 分钟阅读

第 23 章:并发容器与原子类 ​

学习目标 ​

  • 掌握 ConcurrentHashMap / CopyOnWriteArrayList
  • 学会用 AtomicInteger / AtomicLong 做无锁计数
  • 理解 CountDownLatch / CompletableFuture 简化并发

一、并发容器速查 ​

HashMap / ArrayList 都是线程不安全的,多线程下要用并发版本:

场景线程不安全线程安全(推荐)
键值对HashMapConcurrentHashMap
列表(读多写少)ArrayListCopyOnWriteArrayList
队列LinkedListConcurrentLinkedQueue / ArrayBlockingQueue
有序键值TreeMapConcurrentSkipListMap
java
// ✅ 多线程下用 ConcurrentHashMap
Map<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1);     // 线程安全,无需加锁
int v = map.getOrDefault("a", 0);

// ✅ 读多写少场景(如配置、黑名单)
List<String> blacklist = new CopyOnWriteArrayList<>();
blacklist.add("user1");    // 写时复制整个数组,读无锁,极快

⚠️ 坑 1:不要用 Hashtable / Collections.synchronizedMap——它们锁整个表,性能差。直接用 ConcurrentHashMap。

二、原子类(无锁计数器) ​

java
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet();            // ++i
count.getAndIncrement();            // i++
count.addAndGet(10);                // += 10
count.compareAndSet(5, 100);        // CAS:期望 5 就改成 100

// 高并发计数用 LongAdder(分段累加,比 AtomicLong 快 5-10 倍)
LongAdder adder = new LongAdder();
adder.increment();
long sum = adder.sum();

⚠️ 坑 2:count++ 在多线程下不是原子的(3 步:read → +1 → write),必须用 incrementAndGet() 才有原子性。

三、CountDownLatch(倒计时器) ​

等 N 个任务都完成,再继续:

java
CountDownLatch latch = new CountDownLatch(3);  // 计数 3

for (int i = 1; i <= 3; i++) {
    new Thread(() -> {
        try {
            // 干活
        } finally {
            latch.countDown();    // 计数 -1
        }
    }).start();
}

latch.await();   // 阻塞,等计数到 0 才继续
System.out.println("所有任务完成");

四、CompletableFuture(异步编排,JDK 8+) ​

比 Future 更强大,支持链式调用:

java
// 异步执行 + 链式处理
CompletableFuture.supplyAsync(() -> queryUser(userId))     // ① 异步查用户
    .thenApply(user -> queryOrders(user.getId()))          // ② 拿到用户后查订单
    .thenAccept(orders -> System.out.println(orders))     // ③ 最终处理
    .exceptionally(ex -> {                                // ④ 异常兜底
        log.error("查询失败", ex);
        return null;
    });

// 等待所有完成
CompletableFuture<Void> all = CompletableFuture.allOf(future1, future2, future3);
all.join();    // 阻塞等所有完成

和线程池配合:

java
ExecutorService pool = Executors.newFixedThreadPool(4);
CompletableFuture.supplyAsync(() -> queryData(), pool)   // 用指定线程池
                  .thenAccept(data -> process(data));

五、并发实战:批量查询 ​

java
// ❌ 串行:慢
for (Long id : ids) {
    User u = userService.findById(id);
    process(u);
}

// ✅ 并行:用 CompletableFuture
List<CompletableFuture<User>> futures = ids.stream()
    .map(id -> CompletableFuture.supplyAsync(
        () -> userService.findById(id), pool))
    .collect(Collectors.toList());

CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

List<User> users = futures.stream()
    .map(CompletableFuture::join)
    .collect(Collectors.toList());

六、什么时候用什么 ​

场景用什么
多线程改 MapConcurrentHashMap
高并发计数LongAdder
简单计数 / 标志位AtomicInteger
等 N 个任务完成CountDownLatch
异步任务编排CompletableFuture
读多写少 ListCopyOnWriteArrayList
生产消费队列ArrayBlockingQueue

七、本章小结 ​

要点关键
并发 MapConcurrentHashMap,别用 Hashtable / synchronizedMap
原子类AtomicInteger / LongAdder,无锁快
倒计时CountDownLatch 等 N 个任务完成
异步编排CompletableFuture 链式调用
高并发计数LongAdder,比 AtomicLong 快 5-10 倍

动手练习 ​

  1. 并发计数器:1000 个线程用 AtomicInteger 各加 100 次,验证最终 100000
  2. 批量查询:用 CompletableFuture.allOf 并行查 10 个用户
  3. 黑名单:用 CopyOnWriteArrayList 实现一个读多写少的黑名单

下一章:第 24 章:Lambda 与 Stream 实战 →

本站基于 VitePress 构建 · 由 StackHub 团队维护