Skip to content

并发优化

代码能跑不等于代码好。

并发程序尤其如此——100 个线程未必比 10 个线程快。优化方向错了,越努力越糟糕。

优化的四个方向

方向方法效果
减少锁竞争减小锁粒度、无锁算法
减少阻塞异步、非阻塞
减少上下文切换合理线程数
提高缓存命中率数据局部性

减少锁竞争

锁是最常见的性能瓶颈。

错误:粗粒度锁

java
public class BadLockExample {

    private final Object lock = new Object();
    private int a = 0, b = 0, c = 0;

    // ❌ 一个锁锁所有操作,线程全排队
    public void updateA() {
        synchronized (lock) { a++; }
    }

    public void updateB() {
        synchronized (lock) { b++; }
    }

    public void updateC() {
        synchronized (lock) { c++; }
    }
}

正确:细粒度锁 / 无锁

java
public class FineGrainedLockExample {

    // ✅ 每个变量独立锁
    private final AtomicInteger a = new AtomicInteger();
    private final AtomicInteger b = new AtomicInteger();
    private final AtomicInteger c = new AtomicInteger();

    // 无锁操作
    public void updateA() { a.incrementAndGet(); }
    public void updateB() { b.incrementAndGet(); }
    public void updateC() { c.incrementAndGet(); }
}

正确:分段锁

java
public class SegmentLockExample {

    // 分段数组,每段独立锁
    private static final int SEGMENTS = 16;
    private final Node[] segments;

    public SegmentLockExample() {
        segments = new Node[SEGMENTS];
    }

    private Node segmentFor(Object key) {
        int hash = key.hashCode() & (SEGMENTS - 1);
        synchronized (segments[hash]) {
            // ...
        }
    }
}

减少阻塞

阻塞意味着线程空转。

异步 vs 同步

java
public class AsyncVsSync {

    // ❌ 同步:线程阻塞等待 I/O
    public String syncGet(String url) {
        try {
            return new URL(url).openStream()  // 阻塞
                .readAllBytes().toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    // ✅ 异步:不阻塞线程
    public CompletableFuture<String> asyncGet(String url) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return new URL(url).openStream()
                    .readAllBytes().toString();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
    }

    public static void main(String[] args) {
        AsyncVsSync demo = new AsyncVsSync();

        // 同步调用:10 个 URL = 10 个线程 × 100ms = 1000ms
        // 异步调用:1 个线程处理 10 个 = 100ms+
    }
}

非阻塞队列

java
public class NonBlockingExample {

    // ❌ 阻塞队列:take() 会阻塞线程
    private static final BlockingQueue<Task> blockingQueue =
        new LinkedBlockingQueue<>();

    // ✅ 非阻塞队列:poll() 立即返回
    private static final ConcurrentLinkedQueue<Task> nonBlockingQueue =
        new ConcurrentLinkedQueue<>();

    public static void main(String[] args) {
        // 生产者
        for (int i = 0; i < 100; i++) {
            nonBlockingQueue.offer(new Task(i));
        }

        // 消费者(非阻塞轮询)
        Task task;
        while ((task = nonBlockingQueue.poll()) != null) {
            process(task);
        }
    }

    private static void process(Task task) {}
}

减少上下文切换

线程数过多会导致大量上下文切换。

合理线程数

java
public class OptimalThreadCount {

    public static void main(String[] args) {
        int cores = Runtime.getRuntime().availableProcessors();
        System.out.println("CPU 核心数: " + cores);

        // CPU 密集型:线程数 = 核心数
        System.out.println("CPU 密集型线程数: " + cores);

        // I/O 密集型:线程数 = 核心数 × (1 + 等待时间/计算时间)
        // 假设等待时间是计算时间的 3 倍
        System.out.println("I/O 密集型线程数: " + cores * 4);
    }
}

减少 synchronized 范围

java
public class ReduceSyncScope {

    private final Object lock = new Object();
    private int sharedValue;

    // ❌ 同步范围过大
    public void badMethod() {
        synchronized (lock) {
            // 同步整个方法
            calculate();
            validate();
            sharedValue = result;
        }
    }

    // ✅ 同步范围最小化
    public void goodMethod() {
        int temp;
        // 不需要同步的计算
        temp = calculate();
        temp = validate(temp);
        // 只同步必要的写入
        synchronized (lock) {
            sharedValue = temp;
        }
    }
}

LongAdder:减少 CAS 竞争

高并发计数器,AtomicLong 有瓶颈。

java
public class LongAdderOptimization {

    // ❌ AtomicLong:所有线程竞争同一个 value
    private static final AtomicLong counter1 = new AtomicLong();

    // ✅ LongAdder:分段累加,减少竞争
    private static final LongAdder counter2 = new LongAdder();

    public static void main(String[] args) throws InterruptedException {
        int threads = 100;
        int iterations = 10000;
        ExecutorService executor = Executors.newFixedThreadPool(threads);

        // AtomicLong 性能测试
        long start = System.nanoTime();
        for (int i = 0; i < threads; i++) {
            executor.submit(() -> {
                for (int j = 0; j < iterations; j++) {
                    counter1.incrementAndGet();
                }
            });
        }
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.MINUTES);
        System.out.println("AtomicLong: " + counter1.get() +
            ", 耗时: " + (System.nanoTime() - start) / 1_000_000 + "ms");
    }
}

读写分离

读多写少场景,优化读性能。

java
public class ReadWriteSeparation {

    // ❌ 普通 ConcurrentHashMap:读写都要竞争
    private static final ConcurrentHashMap<String, String> map1 =
        new ConcurrentHashMap<>();

    // ✅ CopyOnWriteArrayList:读无锁,写时复制
    private static final CopyOnWriteArrayList<String> list =
        new CopyOnWriteArrayList<>();

    public static void main(String[] args) {
        // 填充数据
        for (int i = 0; i < 10000; i++) {
            list.add("item-" + i);
        }

        // 100 个线程同时读取(无锁)
        ExecutorService readers = Executors.newFixedThreadPool(100);
        for (int i = 0; i < 100; i++) {
            readers.submit(() -> {
                for (int j = 0; j < 1000; j++) {
                    list.contains("item-" + j);
                }
            });
        }
        readers.shutdown();
    }
}

批量操作

减少操作次数。

java
public class BatchOperation {

    // ❌ 逐个插入:1000 次网络往返
    public void badBatchInsert(List<User> users) {
        for (User user : users) {
            db.insert(user);  // 每次都要网络往返
        }
    }

    // ✅ 批量插入:1 次网络往返
    public void goodBatchInsert(List<User> users, int batchSize) {
        List<User> batch = new ArrayList<>();
        for (User user : users) {
            batch.add(user);
            if (batch.size() >= batchSize) {
                db.batchInsert(batch);  // 批量插入
                batch.clear();
            }
        }
        if (!batch.isEmpty()) {
            db.batchInsert(batch);
        }
    }
}

优化检查清单

检查项问题优化
锁粒度一个锁锁所有分离锁或无锁
锁范围同步整个方法只同步必要代码
阻塞调用同步 I/O异步 I/O
线程数固定 100 线程根据 CPU/IO 调整
计数器AtomicLongLongAdder
读写比读多写少用 CHM考虑 CopyOnWrite

注意事项

  1. 先测量再优化:用 profiler 找瓶颈,不要猜
  2. 避免过早优化:可读性优先,不确定时不要优化
  3. 优先减少锁竞争:收益最高
  4. 一致性 vs 性能:CAP 权衡,合适就好

要点回顾

  • 减少锁竞争:减小锁粒度、无锁算法、分段锁
  • 减少阻塞:异步 I/O、非阻塞队列
  • 减少上下文切换:合理线程数
  • LongAdder:高并发计数器的首选
  • 读写分离:读多写少用 CopyOnWrite
  • 批量操作:减少网络往返

相关链接

基于 VitePress 构建