Java 并发编程与 JUC 包深度指南

深入解析 Java JUC 并发包:从 Lock 体系、同步工具到 CompletableFuture 异步编排与虚拟线程 Project Loom,配合完整代码与性能对比。

Java 并发编程是现代后端开发的核心能力之一。本文将系统梳理 Java 并发技术从 synchronizedLock、再到 JUC(java.util.concurrent)工具包,直至 Project Loom 虚拟线程的完整演进路线。每个章节均配有可直接编译运行的完整代码与性能分析思路。

前置知识:本指南面向已掌握 Java 基础语法的开发者,建议预先了解线程创建方式(ThreadRunnableCallable)与基本同步概念。


Java并发演进(synchronized→Lock→JUC→虚拟线程)

Java 并发模型的演进大致可分为四个阶段:

  1. JDK 1.0 / 1.2 时代synchronized(锁对象) + wait/notify
  2. JDK 1.5(Java 5)时代:引入 JUC 包,LockConcurrentHashMapAtomic 原子类、ExecutorService
  3. JDK 1.7 / 1.8 时代ForkJoinPoolCompletableFutureStampedLock
  4. JDK 19+(Project Loom 预览):虚拟线程(Virtual Threads)、结构化并发(StructuredTaskScope)、作用域值(ScopedValue

这种演进并非替代关系,而是层层叠加。在简单场景下 synchronized 依然足够;在高竞争、高灵活性场景中,JUC 工具包提供了更细粒度的控制。虚拟线程则试图用"数百万级轻量线程"颠覆传统线程池模型。


Lock体系与AQS源码(ReentrantLock, ReadWriteLock, StampedLock, Condition)

AQS 核心设计

AbstractQueuedSynchronizer(AQS)是 JUC 中几乎所有同步器(ReentrantLockCountDownLatchSemaphore)的底层基石。它维护一个 volatile int state 和一个 FIFO 双向队列(CLH 变种),通过 CAS 操作尝试修改 state,失败则将当前线程封装为 Node 入队并阻塞。

AQS 模板方法设计将"资源获取/释放逻辑"交给子类实现:

  • tryAcquire(int arg) / tryRelease(int arg):独占模式
  • tryAcquireShared(int arg) / tryReleaseShared(int arg):共享模式

ReentrantLock 示例

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 演示 ReentrantLock 的可重入、可中断、超时获取特性
 */
public class ReentrantLockDemo {
    // 公平锁:按请求顺序获取,吞吐量略低
    private final Lock lock = new ReentrantLock(true);
    private int count = 0;

    public void increment() {
        lock.lock(); // 获取锁
        try {
            // 可重入:同一线程可再次获取该锁而不会死锁
            count++;
            System.out.println(Thread.currentThread().getName() + " 计数: " + count);
        } finally {
            lock.unlock(); // 必须在 finally 中释放,避免异常导致死锁
        }
    }

    // 演示 tryLock 超时获取
    public boolean tryIncrementWithTimeout() throws InterruptedException {
        if (lock.tryLock(2, java.util.concurrent.TimeUnit.SECONDS)) {
            try {
                count++;
                return true;
            } finally {
                lock.unlock();
            }
        }
        System.out.println(Thread.currentThread().getName() + " 获取锁超时");
        return false;
    }

    public static void main(String[] args) {
        ReentrantLockDemo demo = new ReentrantLockDemo();
        Runnable task = () -> {
            for (int i = 0; i < 5; i++) {
                demo.increment();
            }
        };

        Thread t1 = new Thread(task, "线程-A");
        Thread t2 = new Thread(task, "线程-B");
        t1.start();
        t2.start();
    }
}

Lock 与 synchronized 对比

特性Lock(如 ReentrantLock)synchronized
获取/释放方式显示 lock() / unlock()JVM 隐式管理(monitorenter/monitorexit)
可重入性支持支持
公平性ReentrantLock(true) 支持公平锁非公平锁
可中断获取lockInterruptibly() 支持不支持
超时获取tryLock(long, TimeUnit) 支持不支持
条件队列支持多个 Condition仅一个 wait/notify 条件集合
性能高竞争下 CAS + AQS 队列更优JDK 6+ 优化后接近,但功能受限
语法简洁度需写 try-finally,易出错语法简洁,自动释放

Condition 精准唤醒

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 使用 Condition 实现生产者-消费者模式,支持多条件精准唤醒
 */
public class ConditionProducerConsumer {
    private final Lock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();  // 队列未满条件
    private final Condition notEmpty = lock.newCondition(); // 队列非空条件
    private final int[] buffer = new int[10];
    private int count = 0;
    private int putIndex = 0;
    private int takeIndex = 0;

    public void produce(int value) throws InterruptedException {
        lock.lock();
        try {
            // 当缓冲区满时,生产者等待
            while (count == buffer.length) {
                notFull.await();
            }
            buffer[putIndex] = value;
            putIndex = (putIndex + 1) % buffer.length;
            count++;
            System.out.println(Thread.currentThread().getName() + " 生产: " + value + ",当前库存: " + count);
            // 只唤醒等待消费的线程
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    public int consume() throws InterruptedException {
        lock.lock();
        try {
            // 当缓冲区空时,消费者等待
            while (count == 0) {
                notEmpty.await();
            }
            int value = buffer[takeIndex];
            takeIndex = (takeIndex + 1) % buffer.length;
            count--;
            System.out.println(Thread.currentThread().getName() + " 消费: " + value + ",当前库存: " + count);
            // 只唤醒等待生产的线程
            notFull.signal();
            return value;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        ConditionProducerConsumer pc = new ConditionProducerConsumer();
        Thread producer = new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                try {
                    pc.produce(i);
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }, "生产者");

        Thread consumer = new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                try {
                    pc.consume();
                    Thread.sleep(150);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }, "消费者");

        producer.start();
        consumer.start();
    }
}

ReadWriteLock 读写锁

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.HashMap;
import java.util.Map;

/**
 * 演示 ReadWriteLock:读读共享,读写互斥,写写互斥
 * 适用于读多写少的缓存场景
 */
public class ReadWriteLockCache {
    private final ReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Map<String, String> cache = new HashMap<>();

    public String get(String key) {
        // 先加读锁查询缓存
        rwl.readLock().lock();
        try {
            String value = cache.get(key);
            if (value != null) {
                return value;
            }
        } finally {
            rwl.readLock().unlock();
        }

        // 缓存未命中,加写锁加载数据
        rwl.writeLock().lock();
        try {
            // 双重检查:可能其他线程已写入
            String value = cache.get(key);
            if (value == null) {
                value = loadFromDatabase(key);
                cache.put(key, value);
            }
            return value;
        } finally {
            rwl.writeLock().unlock();
        }
    }

    // 模拟从数据库加载
    private String loadFromDatabase(String key) {
        System.out.println("从数据库加载: " + key);
        return "ValueOf" + key;
    }

    public void put(String key, String value) {
        rwl.writeLock().lock();
        try {
            cache.put(key, value);
        } finally {
            rwl.writeLock().unlock();
        }
    }

    public static void main(String[] args) {
        ReadWriteLockCache cache = new ReadWriteLockCache();
        // 启动多个读线程
        for (int i = 0; i < 5; i++) {
            new Thread(() -> System.out.println("读取: " + cache.get("key1")), "读线程" + i).start();
        }
        // 启动写线程
        new Thread(() -> cache.put("key1", "新值"), "写线程").start();
    }
}

StampedLock 乐观读锁

import java.util.concurrent.locks.StampedLock;

/**
 * StampedLock 支持三种模式:读锁、写锁、乐观读
 * 乐观读不会阻塞写操作,适合读极多、写极少的场景
 */
public class StampedLockDemo {
    private final StampedLock lock = new StampedLock();
    private double x = 0.0;
    private double y = 0.0;

    // 乐观读:先读数据,再验证stamp是否被写过
    public double distanceFromOrigin() {
        long stamp = lock.tryOptimisticRead(); // 获取乐观读戳
        double currentX = x;
        double currentY = y;
        // 验证stamp:若期间有写操作,stamp会变化
        if (!lock.validate(stamp)) {
            // 被污染了,升级为悲观读锁
            stamp = lock.readLock();
            try {
                currentX = x;
                currentY = y;
            } finally {
                lock.unlockRead(stamp);
            }
        }
        return Math.sqrt(currentX * currentX + currentY * currentY);
    }

    public void move(double deltaX, double deltaY) {
        long stamp = lock.writeLock();
        try {
            x += deltaX;
            y += deltaY;
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    public static void main(String[] args) {
        StampedLockDemo point = new StampedLockDemo();
        point.move(3, 4);
        System.out.println("距离原点: " + point.distanceFromOrigin());
    }
}

同步工具类(CountDownLatch/CyclicBarrier/Semaphore/Exchanger/Phaser)

CountDownLatch 等待多线程完成

import java.util.concurrent.CountDownLatch;

/**
 * CountDownLatch:一个或多个线程等待其他线程完成操作后再执行
 * 倒计时器不可重置,用完后需重新创建实例
 */
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        int workerCount = 3;
        CountDownLatch latch = new CountDownLatch(workerCount);

        for (int i = 0; i < workerCount; i++) {
            final int id = i;
            new Thread(() -> {
                System.out.println("工作者 " + id + " 开始工作...");
                try {
                    Thread.sleep((id + 1) * 1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                System.out.println("工作者 " + id + " 完成工作");
                latch.countDown(); // 计数减一
            }).start();
        }

        System.out.println("主线程等待所有工作者完成...");
        latch.await(); // 阻塞,直到计数归零
        System.out.println("所有工作者已完成,主线程继续执行");
    }
}

CyclicBarrier 循环屏障

import java.util.concurrent.CyclicBarrier;

/**
 * CyclicBarrier:让一组线程到达屏障后互相等待,直到所有线程都到达后才继续执行
 * 与 CountDownLatch 不同:CyclicBarrier 可重置(reset),支持重复使用
 */
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        int party = 3;
        // 所有线程到达后执行一次 Runnable(屏障操作)
        CyclicBarrier barrier = new CyclicBarrier(party, () -> {
            System.out.println("--- 所有线程已到达屏障,本轮汇合完成 ---");
        });

        Runnable task = () -> {
            for (int round = 1; round <= 2; round++) {
                try {
                    System.out.println(Thread.currentThread().getName() + " 正在执行第 " + round + " 轮任务...");
                    Thread.sleep((int) (Math.random() * 2000));
                    System.out.println(Thread.currentThread().getName() + " 到达屏障");
                    barrier.await(); // 到达屏障并等待
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };

        for (int i = 0; i < party; i++) {
            new Thread(task, "队员-" + i).start();
        }
    }
}

Semaphore 流量控制

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * Semaphore:信号量,控制同时访问某个资源的线程数量
 * 常用于限流,如数据库连接池控制
 */
public class SemaphoreDemo {
    private final Semaphore semaphore = new Semaphore(3); // 最多允许3个线程同时访问

    public void accessResource(int threadId) {
        try {
            // 获取许可,若当前无可用许可则阻塞
            semaphore.acquire();
            System.out.println("线程 " + threadId + " 获取许可,开始访问资源");
            TimeUnit.SECONDS.sleep(2);
            System.out.println("线程 " + threadId + " 释放许可,访问结束");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            semaphore.release(); // 释放许可,供其他线程使用
        }
    }

    public static void main(String[] args) {
        SemaphoreDemo demo = new SemaphoreDemo();
        for (int i = 0; i < 8; i++) {
            final int id = i;
            new Thread(() -> demo.accessResource(id), "Thread-" + i).start();
        }
    }
}

Exchanger 线程交换数据

import java.util.concurrent.Exchanger;

/**
 * Exchanger:两个线程在Exchange点交换数据
 * 典型应用:遗传算法、管道数据校对
 */
public class ExchangerDemo {
    public static void main(String[] args) {
        Exchanger<String> exchanger = new Exchanger<>();

        Thread threadA = new Thread(() -> {
            try {
                String dataA = "来自线程A的数据";
                System.out.println("线程A准备交换数据: " + dataA);
                // 到达交换点后阻塞,直到线程B也到达
                String received = exchanger.exchange(dataA);
                System.out.println("线程A收到: " + received);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, "线程A");

        Thread threadB = new Thread(() -> {
            try {
                Thread.sleep(2000); // 模拟延迟
                String dataB = "来自线程B的数据";
                System.out.println("线程B准备交换数据: " + dataB);
                String received = exchanger.exchange(dataB);
                System.out.println("线程B收到: " + received);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, "线程B");

        threadA.start();
        threadB.start();
    }
}

Phaser 动态注册分阶段

import java.util.concurrent.Phaser;

/**
 * Phaser:支持动态注册/注销参与者的分阶段同步器
 * 比 CountDownLatch 和 CyclicBarrier 更灵活,支持多阶段
 */
public class PhaserDemo {
    public static void main(String[] args) {
        Phaser phaser = new Phaser(1); // 注册主线程

        for (int i = 0; i < 3; i++) {
            phaser.register(); // 动态注册参与者
            new Thread(new Worker(phaser, i)).start();
        }

        System.out.println("主线程等待所有参与者完成阶段0...");
        phaser.arriveAndAwaitAdvance(); // 主线程到达并等待
        System.out.println("阶段0完成,进入阶段1...");
        phaser.arriveAndAwaitAdvance();
        System.out.println("阶段1完成");
        phaser.arriveAndDeregister(); // 主线程注销
    }

    static class Worker implements Runnable {
        private final Phaser phaser;
        private final int id;

        Worker(Phaser phaser, int id) {
            this.phaser = phaser;
            this.id = id;
        }

        @Override
        public void run() {
            System.out.println("工作者 " + id + " 到达阶段0");
            phaser.arriveAndAwaitAdvance(); // 到达阶段0并等待

            // 阶段1工作
            System.out.println("工作者 " + id + " 执行阶段1任务...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }

            System.out.println("工作者 " + id + " 到达阶段1");
            phaser.arriveAndDeregister(); // 到达阶段1并注销
        }
    }
}

同步工具对比表

工具类核心作用是否可重用典型场景
CountDownLatch等待其他线程完成否(一次性)启动服务前等待依赖初始化、多任务并行后汇总
CyclicBarrier多线程互相等待汇聚是(循环重置)分阶段计算(如 MapReduce 的 map 阶段)
Semaphore控制并发访问数量限流、资源池(连接池、线程池)
Exchanger两线程交换数据流水线数据校对、遗传算法
Phaser多阶段动态注册同步复杂多阶段任务,需动态增减参与者

原子类与CAS(AtomicInteger, LongAdder, ABA问题, AtomicStampedReference)

基本原子类

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * AtomicInteger 基于 CAS(Compare-And-Swap)实现无锁线程安全
 * 底层调用 Unsafe.compareAndSwapInt,依赖 CPU 原子指令
 */
public class AtomicDemo {
    private final AtomicInteger counter = new AtomicInteger(0);
    private final AtomicReference<String> name = new AtomicReference<>("初始值");

    public void increment() {
        // getAndIncrement 等价于 i++ 的原子版本
        int newValue = counter.getAndIncrement();
        System.out.println(Thread.currentThread().getName() + " 递增后: " + newValue);
    }

    // 乐观锁模式:CAS 更新,失败则重试
    public void casUpdateName(String expected, String newName) {
        boolean updated = name.compareAndSet(expected, newName);
        System.out.println("CAS更新" + (updated ? "成功" : "失败") + ",当前值: " + name.get());
    }

    public static void main(String[] args) {
        AtomicDemo demo = new AtomicDemo();
        for (int i = 0; i < 5; i++) {
            new Thread(demo::increment, "线程-" + i).start();
        }

        demo.casUpdateName("初始值", "新值A");
        demo.casUpdateName("初始值", "新值B"); // 预期已变,CAS失败
    }
}

LongAdder 高并发累加

import java.util.concurrent.atomic.LongAdder;

/**
 * LongAdder 在高并发下性能优于 AtomicLong
 * 原理:分散热点,内部维护 Cell[] 数组,不同线程操作不同 Cell,最后汇总
 * Serial(低竞争)场景 AtomicLong 更优,因 LongAdder 有额外汇总开销
 */
public class LongAdderDemo {
    private final LongAdder adder = new LongAdder();

    public void increment() {
        adder.increment(); // 底层无锁,分散竞争
    }

    public long getSum() {
        return adder.sum(); // 获取当前总和
    }

    public static void main(String[] args) throws InterruptedException {
        LongAdderDemo demo = new LongAdderDemo();
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    demo.increment();
                }
            });
            threads[i].start();
        }
        for (Thread t : threads) {
            t.join();
        }
        System.out.println("最终累加结果: " + demo.getSum());
    }
}

ABA 问题与 AtomicStampedReference

import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * ABA 问题:线程A读取值为A,线程B改为B又改回A,线程A的CAS仍能成功,但值已变化过
 * AtomicStampedReference 用版本号(stamp)解决,每次修改 stamp 自增
 */
public class AbaProblemDemo {
    public static void main(String[] args) throws InterruptedException {
        // 初始值 100,版本号 0
        AtomicStampedReference<Integer> value = new AtomicStampedReference<>(100, 0);

        int[] stampHolder = new int[1];
        Integer current = value.get(stampHolder);
        int initialStamp = stampHolder[0];
        System.out.println("初始值: " + current + ",stamp: " + initialStamp);

        // 线程B:制造 ABA 问题(100→101→100)
        Thread threadB = new Thread(() -> {
            int[] holder = new int[1];
            value.get(holder);
            int stamp = holder[0];
            // 第一次 CAS:100→101
            value.compareAndSet(100, 101, stamp, stamp + 1);
            // 获取最新 stamp
            value.get(holder);
            stamp = holder[0];
            // 第二次 CAS:101→100
            value.compareAndSet(101, 100, stamp, stamp + 1);
            System.out.println("线程B完成 ABA 操纵,当前stamp: " + value.get(holder));
        });

        threadB.start();
        threadB.join();

        // 线程A:用旧 stamp 尝试 CAS,会失败
        boolean result = value.compareAndSet(100, 200, initialStamp, initialStamp + 1);
        System.out.println("线程A用旧stamp CAS结果: " + result); // false,因为 stamp 已变

        // 用当前最新 stamp 重试
        int[] finalHolder = new int[1];
        value.get(finalHolder);
        result = value.compareAndSet(100, 200, finalHolder[0], finalHolder[0] + 1);
        System.out.println("线程A用新stamp CAS结果: " + result); // true
    }
}

Unsafe 模拟 CAS(仅供学习)

import sun.misc.Unsafe;
import java.lang.reflect.Field;

/**
 * 使用 Unsafe 理解底层 CAS 实现
 * 生产环境不应直接使用 Unsafe,应使用封装好的原子类
 */
public class UnsafeCasDemo {
    private volatile long value = 0;
    private static final Unsafe UNSAFE;
    private static final long VALUE_OFFSET;

    static {
        try {
            // 通过反射获取 Unsafe 实例
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            UNSAFE = (Unsafe) field.get(null);
            // 获取 value 字段的内存偏移量
            VALUE_OFFSET = UNSAFE.objectFieldOffset(UnsafeCasDemo.class.getDeclaredField("value"));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public boolean compareAndSwap(long expected, long newValue) {
        // 底层调用 CPU cmpxchg 指令
        return UNSAFE.compareAndSwapLong(this, VALUE_OFFSET, expected, newValue);
    }

    public static void main(String[] args) {
        UnsafeCasDemo demo = new UnsafeCasDemo();
        boolean result = demo.compareAndSwap(0, 1);
        System.out.println("首次 CAS 结果: " + result + ",当前值: " + demo.value);
        result = demo.compareAndSwap(0, 2);
        System.out.println("二次 CAS 结果: " + result + ",当前值: " + demo.value);
    }
}

CompletableFuture异步编排(thenCompose/thenCombine/allOf/anyOf/异常处理)

CompletableFuture vs Future 对比

特性FutureCompletableFuture
获取结果方式get() 阻塞 / isDone() 轮询get() / join() / 回调驱动(非阻塞)
任务编排不支持链式组合支持 thenApply/thenCompose/thenCombine 等链式 API
异常处理get() 抛出 ExecutionException,难以精细处理exceptionally/handle 在链中处理异常
多任务聚合手动管理多个 FutureallOf / anyOf 原生支持
手动完成不支持complete(T) / completeExceptionally(e) 可外部完成
默认线程池提交时的 ExecutorForkJoinPool.commonPool()(也可自定义 Executor)

基本异步任务

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * CompletableFuture 入门:创建、完成、异常处理
 */
public class CompletableFutureBasic {
    public static void main(String[] args) throws Exception {
        // 1. runAsync:无返回值异步任务
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
            System.out.println("runAsync 执行线程: " + Thread.currentThread().getName());
        });

        // 2. supplyAsync:有返回值异步任务
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return "Hello";
        });
        System.out.println("supplyAsync 结果: " + future2.get());

        // 3. 手动完成
        CompletableFuture<String> future3 = new CompletableFuture<>();
        new Thread(() -> future3.complete("手动完成")).start();
        System.out.println("手动完成结果: " + future3.get());
    }
}

复杂链式编排

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * 演示 thenCompose、thenCombine、thenAccept、thenApply 的区别与组合
 */
public class CompletableFutureChain {
    public static void main(String[] args) throws Exception {
        // thenApply:转换结果类型 T -> U
        CompletableFuture<Integer> lengthFuture = CompletableFuture
                .supplyAsync(() -> {
                    System.out.println("步骤1:获取字符串");
                    return "CompletableFuture";
                })
                .thenApply(s -> {
                    System.out.println("步骤2:计算长度");
                    return s.length();
                });
        System.out.println("thenApply 结果: " + lengthFuture.get());

        // thenCompose:将结果扁平化为另一个 CompletableFuture(避免 Future<Future<T>>)
        CompletableFuture<String> composed = CompletableFuture
                .supplyAsync(() -> "user:123")
                .thenCompose(userId -> fetchUserName(userId)); // 返回 CompletableFuture<String>
        System.out.println("thenCompose 结果: " + composed.get());

        // thenCombine:合并两个独立任务的结果
        CompletableFuture<Double> combined = fetchPrice("Apple").thenCombine(
                fetchExchangeRate(),
                (price, rate) -> price * rate // (T, U) -> V
        );
        System.out.println("thenCombine 结果(美元价格): " + combined.get());

        // allOf:等待所有任务完成(无返回值,需自行从各 Future 取结果)
        CompletableFuture<String> taskA = asyncTask("A", 1);
        CompletableFuture<String> taskB = asyncTask("B", 2);
        CompletableFuture<String> taskC = asyncTask("C", 1);
        CompletableFuture<Void> allDone = CompletableFuture.allOf(taskA, taskB, taskC);
        allDone.thenRun(() -> {
            try {
                System.out.println("allOf 全部完成: " + taskA.get() + ", " + taskB.get() + ", " + taskC.get());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).get();

        // anyOf:任一任务完成即返回
        CompletableFuture<Object> anyDone = CompletableFuture.anyOf(
                asyncTask("快任务", 1),
                asyncTask("慢任务", 3)
        );
        System.out.println("anyOf 最先完成: " + anyDone.get());
    }

    static CompletableFuture<String> fetchUserName(String userId) {
        return CompletableFuture.supplyAsync(() -> "NameOf" + userId);
    }

    static CompletableFuture<Double> fetchPrice(String product) {
        return CompletableFuture.supplyAsync(() -> 100.0);
    }

    static CompletableFuture<Double> fetchExchangeRate() {
        return CompletableFuture.supplyAsync(() -> 7.2);
    }

    static CompletableFuture<String> asyncTask(String name, int seconds) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(seconds);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return name + " 完成";
        });
    }
}

异常处理模式

import java.util.concurrent.CompletableFuture;

/**
 * CompletableFuture 异常处理:exceptionally 与 handle 的区别
 * exceptionally:发生异常时提供替代结果,正常情况不执行
 * handle:无论是否异常都执行,类似 try-catch-finally 的结合
 */
public class CompletableFutureException {
    public static void main(String[] args) {
        // 1. exceptionally:仅异常时执行,返回替代值
        CompletableFuture<Integer> withExceptionally = CompletableFuture
                .supplyAsync(() -> 10 / 0) // 触发 ArithmeticException
                .exceptionally(ex -> {
                    System.out.println("捕获异常: " + ex.getMessage());
                    return -1; // 提供降级值
                });
        System.out.println("exceptionally 结果: " + withExceptionally.join());

        // 2. handle:无论成败都处理,接收正常结果或异常
        CompletableFuture<Integer> withHandle = CompletableFuture
                .supplyAsync(() -> 10 / 5)
                .handle((result, ex) -> {
                    if (ex != null) {
                        System.out.println("handle 捕获异常: " + ex.getMessage());
                        return -1;
                    }
                    return result * 2; // 正常情况继续处理
                });
        System.out.println("handle 结果: " + withHandle.join());

        // 3. 结合自定义线程池与超时控制
        java.util.concurrent.Executor executor = java.util.concurrent.Executors.newFixedThreadPool(4);
        CompletableFuture<String> future = CompletableFuture
                .supplyAsync(() -> {
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                    return "太慢了";
                }, executor)
                .orTimeout(2, java.util.concurrent.TimeUnit.SECONDS) // JDK 9+ 超时控制
                .exceptionally(ex -> "超时或异常: " + ex.getClass().getSimpleName());
        System.out.println("超时测试: " + future.join());

        ((java.util.concurrent.ExecutorService) executor).shutdown();
    }
}

ForkJoinPool与工作窃取

ForkJoinPool 基础用法

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

/**
 * ForkJoinPool + RecursiveTask:大任务递归拆分为小任务,多线程并行计算
 * "工作窃取"机制:线程完成自己队列任务后,从其他线程队列尾部窃取任务执行
 * 适用于:递归可分解的计算密集型任务(大数据求和、排序、MapReduce)
 */
public class ForkJoinSum extends RecursiveTask<Long> {
    private static final int THRESHOLD = 10000; // 拆分阈值
    private final long[] numbers;
    private final int start;
    private final int end;

    public ForkJoinSum(long[] numbers, int start, int end) {
        this.numbers = numbers;
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        int length = end - start;
        if (length <= THRESHOLD) {
            // 任务足够小,直接计算
            long sum = 0;
            for (int i = start; i < end; i++) {
                sum += numbers[i];
            }
            return sum;
        }

        // 拆分任务
        int middle = start + length / 2;
        ForkJoinSum leftTask = new ForkJoinSum(numbers, start, middle);
        ForkJoinSum rightTask = new ForkJoinSum(numbers, middle, end);

        // 异步执行左子任务
        leftTask.fork();
        // 当前线程直接执行右子任务(减少调度开销)
        long rightResult = rightTask.compute();
        // 等待左子任务结果并合并
        long leftResult = leftTask.join();

        return leftResult + rightResult;
    }

    public static void main(String[] args) {
        long[] numbers = new long[100_000_000];
        for (int i = 0; i < numbers.length; i++) {
            numbers[i] = i + 1;
        }

        ForkJoinPool pool = new ForkJoinPool();
        long startTime = System.currentTimeMillis();
        long result = pool.invoke(new ForkJoinSum(numbers, 0, numbers.length));
        long endTime = System.currentTimeMillis();
        System.out.println("ForkJoin 求和结果: " + result);
        System.out.println("耗时: " + (endTime - startTime) + " ms");
        pool.shutdown();
    }
}

RecursiveAction 示例

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

/**
 * RecursiveAction 用于无返回值的 ForkJoin 任务
 * 演示:并行初始化大数组
 */
public class ForkJoinInit extends RecursiveAction {
    private static final int THRESHOLD = 10000;
    private final double[] array;
    private final int start;
    private final int end;

    ForkJoinInit(double[] array, int start, int end) {
        this.array = array;
        this.start = start;
        this.end = end;
    }

    @Override
    protected void compute() {
        if (end - start <= THRESHOLD) {
            for (int i = start; i < end; i++) {
                array[i] = Math.sin(i) * Math.cos(i);
            }
        } else {
            int mid = (start + end) / 2;
            ForkJoinInit left = new ForkJoinInit(array, start, mid);
            ForkJoinInit right = new ForkJoinInit(array, mid, end);
            invokeAll(left, right); // 同时触发两个子任务
        }
    }

    public static void main(String[] args) {
        double[] array = new double[10_000_000];
        ForkJoinPool pool = new ForkJoinPool();
        pool.invoke(new ForkJoinInit(array, 0, array.length));
        System.out.println("数组初始化完成,样本值: " + array[100]);
        pool.shutdown();
    }
}

虚拟线程Project Loom(结构化并发, ScopedValue)

虚拟线程基础

import java.util.concurrent.Executors;
import java.time.Duration;

/**
 * Project Loom 虚拟线程(JDK 21 正式特性)
 * 虚拟线程由 JVM 调度,而非操作系统,创建成本极低(~数百字节)
 * 适合 IO 密集型高并发场景,一个平台线程可承载数百万虚拟线程
 * 注意:虚拟线程不应与 synchronized 内联(pinning 平台线程)混用,建议使用 ReentrantLock
 */
public class VirtualThreadDemo {
    public static void main(String[] args) throws Exception {
        // 1. 使用静态工厂创建虚拟线程
        Thread vThread = Thread.startVirtualThread(() -> {
            System.out.println("虚拟线程运行中: " + Thread.currentThread());
        });
        vThread.join();

        // 2. 使用 ExecutorService 批量创建(try-with-resources 自动关闭)
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i < 100_000; i++) {
                final int id = i;
                executor.submit(() -> {
                    // 模拟 IO 操作
                    try {
                        Thread.sleep(Duration.ofMillis(10));
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                    if (id % 20000 == 0) {
                        System.out.println("任务 " + id + " 完成于 " + Thread.currentThread());
                    }
                });
            }
        } // 自动等待所有虚拟线程完成
        System.out.println("10万个 IO 任务完成");
    }
}

结构化并发

import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.StructuredTaskScope.Subtask;

/**
 * 结构化并发(JDK 21+):子任务生命周期绑定到代码块(try-with-resources)
 * 父任务等待所有子任务完成,任一失败可自动取消其他子任务
 * 相比 "线程烟花"(fire-and-forget),代码结构更清晰,泄漏更少
 */
public class StructuredConcurrencyDemo {

    record User(String name, String address) {}
    record Order(String id, double amount) {}

    // 模拟获取用户信息(可能耗时的 IO 操作)
    private User fetchUser(int userId) throws InterruptedException {
        Thread.sleep(200);
        return new User("用户" + userId, "北京市");
    }

    // 模拟获取订单信息
    private Order fetchOrder(int userId) throws InterruptedException {
        Thread.sleep(300);
        return new Order("ORD-" + userId, 199.99);
    }

    public String getUserSummary(int userId) throws Exception {
        // 在 try 块内创建的作用域,所有子任务关联于此
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            // fork 两个并行子任务
            Subtask<User> userTask = scope.fork(() -> fetchUser(userId));
            Subtask<Order> orderTask = scope.fork(() -> fetchOrder(userId));

            scope.join();           // 等待所有子任务完成
            scope.throwIfFailed();  // 任一任务失败则抛出异常

            // 获取结果(此时已确保两个任务都成功完成)
            User user = userTask.get();
            Order order = orderTask.get();
            return user.name + " 的最新订单: " + order.id + ",金额: " + order.amount;
        }
    }

    public static void main(String[] args) throws Exception {
        StructuredConcurrencyDemo demo = new StructuredConcurrencyDemo();
        String summary = demo.getUserSummary(42);
        System.out.println(summary);
    }
}

ScopedValue 作用域值

import java.util.concurrent.ScopedValue;
import java.util.concurrent.StructuredTaskScope;

/**
 * ScopedValue(JDK 21+预览):线程安全的不可变上下文传递
 * 相比 ThreadLocal:
 * 1. 不可变性避免隐式修改
 * 2. 子任务自动继承父作用域的值(结构化并发中自然传递)
 * 3. 作用域结束后自动清理,无内存泄漏风险
 */
public class ScopedValueDemo {
    // 声明一个 ScopedValue,类似于 ThreadLocal 但不可变
    private static final ScopedValue<String> REQUEST_ID = ScopedValue.newInstance();

    private String processRequest() throws Exception {
        // 在当前代码块内绑定值,退出代码块后自动解绑
        return ScopedValue.where(REQUEST_ID, "REQ-20240901-001").call(() -> {
            System.out.println("主任务获取 RequestID: " + REQUEST_ID.get());
            return handleSubTasks();
        });
    }

    private String handleSubTasks() throws Exception {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            var task1 = scope.fork(() -> {
                // 子任务自动继承父作用域的 ScopedValue
                String id = REQUEST_ID.get();
                System.out.println("子任务1 RequestID: " + id);
                return "子结果A";
            });
            var task2 = scope.fork(() -> {
                String id = REQUEST_ID.get();
                System.out.println("子任务2 RequestID: " + id);
                return "子结果B";
            });
            scope.join();
            return task1.get() + " + " + task2.get();
        }
    }

    public static void main(String[] args) throws Exception {
        ScopedValueDemo demo = new ScopedValueDemo();
        String result = demo.processRequest();
        System.out.println("最终结果: " + result);
    }
}

线程池ThreadPoolExecutor详解

import java.util.concurrent.*;

/**
 * ThreadPoolExecutor 七大参数深度解析:
 * 1. corePoolSize:核心线程数,即使空闲也保留(除非 allowCoreThreadTimeOut)
 * 2. maximumPoolSize:最大线程数
 * 3. keepAliveTime:非核心线程空闲存活时间
 * 4. unit:keepAliveTime 的时间单位
 * 5. workQueue:任务等待队列(ArrayBlockingQueue / LinkedBlockingQueue / SynchronousQueue)
 * 6. threadFactory:创建线程的工厂(可设置线程名称、守护状态)
 * 7. handler:拒绝策略(AbortPolicy / CallerRunsPolicy / DiscardPolicy / DiscardOldestPolicy)
 */
public class ThreadPoolExecutorDemo {
    public static void main(String[] args) {
        // 自定义线程池(阿里规范推荐,避免 Executors 的隐藏风险)
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2,                      // 核心线程数
                5,                      // 最大线程数
                60L,                    // 非核心线程存活时间
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(10), // 容量为10的阻塞队列
                new CustomThreadFactory(),     // 自定义线程工厂
                new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:让调用者线程执行
        );

        // 允许核心线程在空闲时也被回收(默认不回收)
        executor.allowCoreThreadTimeOut(true);

        for (int i = 0; i < 20; i++) {
            final int taskId = i;
            executor.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " 执行任务 " + taskId);
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        // 优雅关闭:先停止接受新任务,再等待已提交任务完成
        executor.shutdown();
        try {
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                executor.shutdownNow(); // 超时则强制中断
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }
    }

    static class CustomThreadFactory implements ThreadFactory {
        private int counter = 0;

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r, "自定义线程-" + (++counter));
            t.setUncaughtExceptionHandler((thread, ex) -> {
                System.out.println("线程 " + thread.getName() + " 发生未捕获异常: " + ex.getMessage());
            });
            return t;
        }
    }
}

线程池工作原理解析

当提交一个新任务时,线程池按以下逻辑处理:

  1. 当前运行线程数 < corePoolSize:创建新核心线程执行任务,即使有空闲线程
  2. 当前运行线程数 >= corePoolSize:任务进入 workQueue 等待
  3. workQueue 已满且运行线程数 < maximumPoolSize:创建临时(非核心)线程执行任务
  4. 运行线程数 >= maximumPoolSize 且队列已满:触发拒绝策略

最佳实践:IO 密集型任务设置较大核心线程数(如 2*N cpu),计算密集型设置接近 N cpu;队列容量不宜无限(如 LinkedBlockingQueue 无界队列易导致 OOM)。


Java内存模型(happens-before, volatile, final语义)

volatile 保证可见性与有序性

import java.util.concurrent.TimeUnit;

/**
 * volatile 关键字的两个语义:
 * 1. 可见性:写 volatile 变量会立即刷新到主内存,读会从主内存刷新
 * 2. 有序性:禁止指令重排序(内存屏障 insert)
 * 注意:volatile 不保证原子性,复合操作(如 i++)仍需 synchronized 或原子类
 */
public class VolatileDemo {
    // 使用 volatile 保证状态变更对所有线程立即可见
    private volatile boolean running = true;
    private volatile int count = 0;

    public void stop() {
        running = false; // 写 volatile,随后插入 StoreLoad 屏障
    }

    public void increment() {
        // 以下不是原子操作,并发下可能丢失更新
        count++; // 分解为:读 -> 修改 -> 写,三步骤
    }

    public static void main(String[] args) throws Exception {
        VolatileDemo demo = new VolatileDemo();

        Thread worker = new Thread(() -> {
            while (demo.running) {
                // 若 running 非 volatile,此循环可能永远看不到主线程的修改
            }
            System.out.println("工作者线程检测到 stop 信号,退出循环");
        });
        worker.start();

        TimeUnit.SECONDS.sleep(1);
        System.out.println("主线程发送 stop 信号");
        demo.stop();
        worker.join();
    }
}

happens-before 规则

Java 内存模型(JMM)定义了 happens-before 关系,确保程序员无需深入理解硬件重排序即可获得可靠的多线程语义。核心规则包括:

  1. 程序次序规则:同一线程中,书写在前面的操作 happens-before 后面的操作
  2. 监视器锁规则unlock happens-before 后面对同一把锁的 lock
  3. volatile 变量规则volatile 写 happens-before 后面对该变量的读
  4. 线程启动规则Thread.start() happens-before 线程中所有操作
  5. 线程终止规则:线程中所有操作 happens-before 线程终止检测(join() 返回、isAlive() 为 false)
  6. 中断规则interrupt() happens-before 检测到中断(isInterrupted()
  7. 对象终结规则:构造函数执行 happens-before finalize()
  8. 传递性:若 A happens-before B,且 B happens-before C,则 A happens-before C

final 安全发布

/**
 * final 字段的内存语义:构造函数中对 final 字段的写入,
 * happens-before 后续每个线程对同一个对象 final 字段的读取。
 * 这意味着 final 字段值一旦构造完成便不可变,且立即可见于其他线程。
 * 注意:不要在构造函数中将 this 引用逸出(escape),否则可能破坏 final 语义。
 */
public class FinalSafePublication {
    private final int value;
    private final String name;

    public FinalSafePublication(int value, String name) {
        this.value = value;
        this.name = name;
        // 错误示例:不要在构造函数内启动线程或注册监听器并传递 this
        // new Thread(() -> System.out.println(this.name)).start(); // this 逸出!
    }

    public int getValue() {
        return value; // 其他线程读到的必然是构造完成后的值
    }

    public String getName() {
        return name;
    }
}

并发最佳实践与死锁排查

死锁示例与预防

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 死锁产生的四个必要条件:互斥、请求与保持、不剥夺、循环等待
 * 预防策略:
 * 1. 按固定顺序获取多把锁
 * 2. 使用 tryLock 超时获取,失败则释放已持有锁
 * 3. 尽量减少锁粒度与持有时间
 */
public class DeadlockPrevention {
    private final Lock lockA = new ReentrantLock();
    private final Lock lockB = new ReentrantLock();

    // 错误示例:循环等待导致死锁
    public void deadlockScenario() {
        Thread t1 = new Thread(() -> {
            lockA.lock();
            try {
                System.out.println("线程1获取锁A");
                TimeUnit.MILLISECONDS.sleep(100);
                lockB.lock();
                try {
                    System.out.println("线程1获取锁B");
                } finally {
                    lockB.unlock();
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lockA.unlock();
            }
        }, "死锁线程1");

        Thread t2 = new Thread(() -> {
            lockB.lock(); // 若与 t1 同时获取不同顺序,可能死锁
            try {
                System.out.println("线程2获取锁B");
                TimeUnit.MILLISECONDS.sleep(100);
                lockA.lock();
                try {
                    System.out.println("线程2获取锁A");
                } finally {
                    lockA.unlock();
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lockB.unlock();
            }
        }, "死锁线程2");

        // 实际运行可能触发死锁,此处仅演示
        // t1.start(); t2.start();
    }

    // 正确示例:固定顺序获取 + tryLock 超时回退
    public void safeOrdering() {
        // 按 hashCode 排序,所有线程按相同顺序获取
        Lock first = lockA.hashCode() < lockB.hashCode() ? lockA : lockB;
        Lock second = lockA.hashCode() < lockB.hashCode() ? lockB : lockA;

        try {
            if (first.tryLock(1, TimeUnit.SECONDS)) {
                try {
                    if (second.tryLock(1, TimeUnit.SECONDS)) {
                        try {
                            System.out.println("成功按序获取两把锁");
                        } finally {
                            second.unlock();
                        }
                    }
                } finally {
                    first.unlock();
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    public static void main(String[] args) {
        DeadlockPrevention demo = new DeadlockPrevention();
        demo.safeOrdering();
    }
}

死锁排查命令

当线上出现疑似死锁时,可使用以下 JDK 工具进行诊断:

# 1. 打印所有线程的堆栈与锁信息,自动检测死锁
jstack -l <pid> > thread_dump.txt

# 2. 在 jstack 输出中搜索 "Found one Java-level deadlock:" 关键字

# 3. 使用 jconsole 或 VisualVM 的 "线程" 面板,查看 "检测死锁" 按钮

# 4. Java Flight Recorder (JFR) 可录制锁竞争事件( jdk.LockContention )

并发编程最佳实践清单

  1. 优先使用高层并发工具ConcurrentHashMap 优于 Collections.synchronizedMapLongAdder 优于 AtomicLong(高竞争),CompletableFuture 优于手动管理 Thread
  2. 避免过度同步:只在必要处加锁,锁粒度尽量小;读写分离场景用 ReadWriteLockStampedLock
  3. 慎用 ThreadLocal:在虚拟线程与线程池环境下容易内存泄漏,考虑 ScopedValue(JDK 21+)替代
  4. 线程安全文档化:公共类应在 Javadoc 中明确标注线程安全策略(不可变、线程安全、有条件线程安全、线程兼容、线程对立)
  5. 超时与取消:长时间阻塞的操作应支持超时(Future.get(timeout)tryLock(long, TimeUnit))与中断响应
  6. 测试并发代码:使用 jcstress(Java Concurrency Stress)工具验证 JMM 假设,而非仅靠单元测试

常见问题(FAQ)

Q1:synchronizedReentrantLock 应该如何选择?

在以下情况优先使用 ReentrantLock:需要尝试非阻塞获取(tryLock)、可中断获取(lockInterruptibly)、可超时获取、需要多个条件队列(Condition)、需要公平锁。否则,对于简单同步块,synchronized 语法更简洁且由 JVM 自动优化,出错概率更低。JDK 19+ 的虚拟线程环境中,长时间的 synchronized 块可能"钉住"(pin)平台线程,建议改用 ReentrantLock

Q2:为什么高并发下 LongAdderAtomicLong 更快?

AtomicLong 所有线程竞争同一变量,大量 CAS 失败后自旋,导致缓存行乒乓(cache line bouncing)。LongAdder 内部维护一个 Cell[] 数组,线程根据 hash 映射到不同 Cell 上进行累加,降低冲突概率。最后调用 sum() 时汇总所有 Cell。这是一种空间换时间、分散热点的设计。

Q3:CompletableFuture 的 thenApplythenCompose 有什么区别?

thenApply 接收一个 Function<T, U>,将当前结果转换为另一种类型,返回 CompletableFuture<U>thenCompose 接收一个 Function<T, CompletableFuture<U>>,用于将当前结果扁平化为另一个异步操作的结果,避免 CompletableFuture<CompletableFuture<U>> 的嵌套。当后续操作本身也是异步 API 时,使用 thenCompose

Q4:什么是虚拟线程的 “pinning” 问题?如何规避?

当虚拟线程执行 synchronized 代码块或调用本地方法(JNI)时,它会被"固定"到底层的平台线程上。如果此时虚拟线程执行的是阻塞 IO,平台线程也会被阻塞,无法去调度其他虚拟线程,从而浪费平台线程资源。规避方法:在虚拟线程中避免使用 synchronized,改用 ReentrantLock;避免在虚拟线程中执行长时间阻塞的 JNI 调用。

Q5:线程池提交任务后,如何优雅地关闭并确保所有任务都已执行完毕?

调用 shutdown() 发起优雅关闭(不再接受新任务,但等待已提交任务完成),然后调用 awaitTermination(timeout, unit) 等待一段时间。如果超时仍有未完成任务,根据业务需求选择继续等待或调用 shutdownNow() 强制中断正在执行的任务。注意 shutdownNow() 返回的任务列表是未被执行的任务,已启动但被中断的任务需要自行处理中断异常。


总结

本文从 synchronized 出发,系统梳理了 Java 并发编程的核心技术栈:

  • Lock 与 AQS:理解可重入锁、读写锁、乐观读的实现差异与适用场景
  • 同步工具类:根据"一次性等待"、“循环汇聚”、“流量控制”、“数据交换”、“多阶段任务"选择合适工具
  • 原子类与 CAS:掌握无锁编程思想,理解 ABA 问题与 LongAdder 的优化原理
  • CompletableFuture:用声明式链式 API 替代回调地狱,优雅处理多任务编排与异常
  • ForkJoinPool:利用工作窃取模型加速可分解的计算密集型任务
  • Project Loom:虚拟线程与结构化并发正在重塑 Java 高并发 IO 编程范式
  • 线程池与 JMM:正确配置线程池参数,理解 happens-before 规则,编写真正线程安全的代码

并发编程没有银弹。每一种锁、每一个工具类都有其特定上下文与权衡。在实际工程中,建议优先使用 JUC 提供的高层抽象,仅在性能关键路径上深入底层优化。配合 jstackJFRjcstress 等工具进行监控与验证,方能在高并发场景下行稳致远。


参考文档:OpenJDK 21 Documentation、《Java并发编程实战》(Goetz 等著)、JEP 444(Virtual Threads)、JEP 453(Structured Concurrency)

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「java」更多文章

  1. Spring Cloud 微服务全栈实践
  2. Spring Security 6.x 与 OAuth2/JWT 安全认证实战
  3. Spring Data JPA 高级指南:关联映射、N+1 与性能优化