Java 多线程开发是 Java 并发编程的核心内容,用于实现程序的并发执行,提高 CPU 利用率和系统吞吐量。以下是 Java 多线程开发的系统性介绍:


线程

一、为什么需要多线程?

  • 提升性能:充分利用多核 CPU 资源。
  • 响应性:UI 线程不阻塞(如 Swing/JavaFX 中后台任务)。
  • 异步处理:如网络请求、文件 I/O、定时任务等。
  • 建模现实问题:如生产者-消费者、银行转账等并发场景。

二、创建线程的两种基本方式

1. 实现 Runnable 接口(推荐)

Runnable task = () -> System.out.println("Hello from thread: " + Thread.currentThread().getName());
Thread t = new Thread(task);
t.start();

2. 继承 Thread 类(不推荐)

class MyThread extends Thread {
    public void run() {
        System.out.println("Running in " + getName());
    }
}
new MyThread().start();

最佳实践:优先使用 Runnable + Thread 或更高级的并发工具(如线程池)。


三、线程的生命周期(6 种状态)

状态 说明
NEW 线程刚创建,未调用 start()
RUNNABLE 可运行(包括正在运行或等待 CPU 时间片)
BLOCKED 等待获取监视器锁(如进入 synchronized 块)
WAITING 无限期等待(如 wait(), join(), LockSupport.park()
TIMED_WAITING 限时等待(如 sleep(1000), wait(1000)
TERMINATED 线程执行完毕或异常终止

可通过 Thread.getState() 查看状态。


四、线程同步机制(解决共享资源竞争)

1. synchronized 关键字

  • 修饰实例方法:锁当前对象(this
  • 修饰静态方法:锁类的 Class 对象
  • 同步代码块synchronized(obj) { ... },可指定任意锁对象

⚠️ 静态与非静态 synchronized 方法不互斥(锁对象不同)。

2. volatile 关键字

  • 保证变量的可见性禁止指令重排序
  • 不保证原子性(如 i++ 仍需同步)

3. java.util.concurrent.locks.Lock

  • 更灵活的显式锁(如 ReentrantLock
  • 支持尝试获取锁、超时、公平锁等
Lock lock = new ReentrantLock();
lock.lock();
try {
    // 临界区
} finally {
    lock.unlock(); // 必须在 finally 中释放
}

五、线程间通信

1. wait() / notify() / notifyAll()

  • 必须在 synchronized 块中调用
  • wait() 释放锁并进入等待队列
  • notify() 唤醒一个等待线程

2. Condition(配合 Lock 使用)

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();

// 等待
lock.lock();
try {
    condition.await();
} finally {
    lock.unlock();
}

// 唤醒
condition.signal(); // 或 signalAll()

六、高级并发工具(java.util.concurrent 包)

1. 线程池(核心!)

避免频繁创建/销毁线程,提升性能。

ExecutorService executor = Executors.newFixedThreadPool(4);
executor.submit(() -> {
    // 任务逻辑
});
executor.shutdown();

常用工厂方法:

  • newFixedThreadPool(n):固定大小线程池
  • newCachedThreadPool():弹性线程池(适合短任务)
  • newSingleThreadExecutor():单线程顺序执行
  • 生产建议:使用 ThreadPoolExecutor 自定义参数(避免 Executors 的隐藏风险)

2. 并发集合

  • ConcurrentHashMap:线程安全的 HashMap
  • CopyOnWriteArrayList:读多写少场景
  • BlockingQueue:如 ArrayBlockingQueue, LinkedBlockingQueue(常用于生产者-消费者)

3. 同步工具类

  • CountDownLatch:等待 N 个任务完成
  • CyclicBarrier:多个线程互相等待到达屏障点
  • Semaphore:控制同时访问资源的线程数
  • CompletableFuture:异步编程(Java 8+)

七、常见问题与最佳实践

❌ 常见陷阱

  • 死锁(多个锁顺序不一致)
  • 活锁 / 饥饿
  • 内存可见性问题(忘记 volatile 或同步)
  • 过度同步(性能下降)

✅ 最佳实践

  1. 优先使用并发工具类(如 ConcurrentHashMap 而非 Collections.synchronizedMap
  2. 缩小同步范围:只同步必要代码
  3. 避免在构造函数中启动线程
  4. 使用线程池管理线程,而非手动创建
  5. 命名线程:便于调试(new Thread(runnable, "MyWorker")
  6. 处理中断:响应 InterruptedException,不要忽略

八、示例:生产者-消费者模型(使用 BlockingQueue

BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);

// 生产者
Thread producer = new Thread(() -> {
    try {
        for (int i = 0; i < 20; i++) {
            queue.put(i); // 阻塞直到有空间
            System.out.println("Produced: " + i);
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

// 消费者
Thread consumer = new Thread(() -> {
    try {
        while (!Thread.currentThread().isInterrupted()) {
            Integer item = queue.take(); // 阻塞直到有元素
            System.out.println("Consumed: " + item);
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

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

总结

Java 多线程开发从基础线程创建到高级并发工具,是一个逐步演进的过程。现代 Java 开发应:

“少用原始 Thread,多用 Executor;少用 synchronized,多用并发集合和原子类;善用 CompletableFuture 实现异步非阻塞。”


虚拟线程

Java 虚拟线程(Virtual Threads) 是 Java 并发编程领域自 JDK 21(2023年发布)以来最具革命性的特性,属于 Project Loom 项目的核心成果。到 2026 年,它已成为高并发后端开发的事实标准,彻底改变了 Java 处理百万级并发的模式。

以下是基于 2026 年最新生产实践的深度解析与避坑指南


🚀 核心概念:什么是虚拟线程?

传统 Java 线程(平台线程)是 1:1 映射 到操作系统内核线程的,创建成本高(约 1MB 内存)、上下文切换开销大。
虚拟线程 则是 M:N 映射(M 个虚拟线程运行在 N 个平台线程上):

  • 轻量级:每个虚拟线程仅占用约 几百字节 栈内存(初始值),可轻松创建百万级实例。
  • 用户态调度:由 JVM 直接调度,无需陷入内核态,极大降低切换成本。
  • 阻塞即挂起:当虚拟线程执行 I/O 阻塞(如 sleep, socket.read)时,JVM 会自动将其卸载(unmount),释放底层的载体线程(Carrier Thread)去执行其他任务,实现“同步代码,异步效果”。

💡 一句话总结:让你用写同步代码的习惯(Thread.sleep, BlockingQueue),获得接近响应式编程(Reactive)的高吞吐能力。


🛠️ 2026 年最佳实践代码

1. 快速启动(JDK 21+)
// 方式一:直接启动(适合临时任务)
Thread.startVirtualThread(() -> {
    System.out.println("Hello from Virtual Thread: " + Thread.currentThread());
});

// 方式二:使用 ExecutorService(推荐,便于管理生命周期)
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 1_000_000).forEach(i -> {
        executor.submit(() -> {
            // 模拟 I/O 阻塞
            Thread.sleep(1000); 
            return i;
        });
    });
} // try-with-resources 自动等待所有任务完成并关闭
2. Spring Boot 3.x 集成(2026 主流配置)

application.properties 中开启虚拟线程支持,Tomcat/Jetty 将自动使用虚拟线程处理请求:

spring.threads.virtual.enabled=true
# 可选:调整载体线程池大小(默认 CPU 核心数)
server.tomcat.threads.max=200 

效果:无需修改 Controller 代码,原本阻塞的 JDBC/Redis 调用现在能支撑十倍以上的并发量。


⚠️ 2026 年生产环境“避坑”指南(关键!)

根据近期社区反馈和架构师实战总结,以下场景需特别警惕:

1. ❌ 严禁用于 CPU 密集型任务

虚拟线程的优势在于 I/O 等待时的挂起。如果是纯计算任务(如视频编码、复杂数学运算):

  • 现象:CPU 占用率飙升,性能甚至低于传统线程池。
  • 原因:虚拟线程不会主动让出 CPU,大量计算任务会占满载体线程,导致调度器频繁空转。
  • 对策:CPU 密集型任务请继续使用 Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())
2. 🔒 慎用 synchronized 和 JNI 阻塞
  • 问题synchronized 锁和某些 native 方法(JNI)会导致虚拟线程 Pinned(钉住) 在载体线程上,无法挂起。
  • 后果:如果大量虚拟线程被钉住,载体线程池会被耗尽,系统退化为传统线程模型,失去高并发优势。
  • 解决方案
    • 使用 java.util.concurrent.locks.ReentrantLock 替代 synchronized
    • 检查第三方库是否包含长时间运行的 native 阻塞调用。
    • 开启 JVM 参数 -Djdk.tracePinnedThreads=full 在开发环境排查钉住线程。
3. 🧵 ThreadLocal 的内存泄漏风险
  • 风险:虚拟线程数量巨大(百万级),如果每个线程都绑定大量 ThreadLocal 数据(如大型用户上下文对象),会导致堆内存爆炸。
  • 新方案:推荐使用 JDK 24+ 正式稳定的 Scoped Values(作用域值)。
    static final ScopedValue<String> USER_ID = ScopedValue.newInstance();
    
    ScopedValue.where(USER_ID, "12345").run(() -> {
        // 在此作用域内共享数据,无内存泄漏风险,且更安全
        processRequest();
    });
    
4. 📉 监控与调试的变化
  • 挑战:传统的 jstack 或线程 Dump 会显示数百万行日志,难以分析。
  • 对策
    • 使用 Java Flight Recorder (JFR) 配合新版 JDK 的虚拟线程事件过滤。
    • 关注指标:虚拟线程创建速率平均存活时间Pinned 线程比例

🆚 虚拟线程 vs WebFlux (响应式编程)

维度 虚拟线程 (Virtual Threads) WebFlux / Reactive
编程模型 同步阻塞 (直观,易调试) 异步回调/流式 (陡峭学习曲线)
生态兼容 完美兼容 (所有旧库可直接用) 需响应式驱动库 (R2DBC, WebClient)
性能表现 I/O 密集型极高,略低于极致优化的 Reactor 极低延迟场景略优,但复杂链路难维护
2026 趋势 首选方案 (90% 场景) 仅限超低延迟/流式处理特定场景

🎯 结论:除非你有极端的低延迟需求或处理无限数据流,否则 2026 年请优先选择虚拟线程。它能以最小的重构成本带来最大的收益。


📈 未来展望 (JDK 26+)

  • 结构化并发 (Structured Concurrency)StructuredTaskScope 将进入正式版,解决多线程任务的生命周期管理难题(如自动取消子任务、统一异常处理)。
  • 全景可观测性:新一代 APM 工具(SkyWalking, Arthas)已全面适配虚拟线程追踪,能清晰展示百万级线程的调用链。
Logo

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

更多推荐