惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News and Events Feed by Topic
Malwarebytes
Malwarebytes
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
Cybersecurity and Infrastructure Security Agency CISA
F
Future of Privacy Forum
C
Cisco Blogs
T
The Exploit Database - CXSecurity.com
A
Arctic Wolf
S
Securelist
K
Kaspersky official blog
S
Schneier on Security
T
ThreatConnect
T
Tenable Blog
Spread Privacy
Spread Privacy
T
True Tiger Recordings
AWS News Blog
AWS News Blog
F
Fox-IT International blog
量子位
T
Threatpost
V
Vulnerabilities – Threatpost
C
CERT Recently Published Vulnerability Notes
Cisco Talos Blog
Cisco Talos Blog
GbyAI
GbyAI
宝玉的分享
宝玉的分享
腾讯CDC
G
Google Developers Blog
aimingoo的专栏
aimingoo的专栏
Cyberwarzone
Cyberwarzone
有赞技术团队
有赞技术团队
S
SegmentFault 最新的问题
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Visual Studio Blog
U
Unit 42
雷峰网
雷峰网
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
O
OpenAI News
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The GitHub Blog
The GitHub Blog
The Register - Security
The Register - Security
MyScale Blog
MyScale Blog
小众软件
小众软件
A
About on SuperTechFans
Last Week in AI
Last Week in AI
Y
Y Combinator Blog
博客园 - 三生石上(FineUI控件)
美团技术团队
Google Online Security Blog
Google Online Security Blog
P
Proofpoint News Feed
MongoDB | Blog
MongoDB | Blog

博客园 - 悠哉大斌

LLM 真的改变了编码风格吗? 如果人工智能会犯错且不精确,为什么它正在改变世界? Python 的协程模型和 JavaScript 的 async/await python闭包和function.__closure__特殊属性 python 的 dunder name和 sunder name 基于nodejs设计REST API的知名开源框架 windows上使用node-oracledb 连接 Oracle 11g Claude Code CLI连接 DeepSeek V4模型后调用报400错误 python里对象(object)到底是什么 Python 泛型演变史 Python 类型别名的演变 Python 类型提示的演变史 PEP 593 新增的Annotated 类型 Alibaba AgentScope 和 microsoft agent framework 详细对比分析 spring AI Alibaba Agent Framework 和 agentscope有什么区别和联系 AI Agent协作模式以及主流开源框架对协作模式的支持 Tailscale 是如何接管 DNS 的? Agent = Model + Harness 使用rust编写typescript编译器的难点在什么地方,哪些数据结构是rust不擅长的? TypeScript/JavaScript 中的异步迭代语句 js中的生成器函数 Tailscale Serve and Funnel openclaw gateway的网络绑定模式 websocket协议和http协议有何依赖关系? MCP通信的双方是谁? claude code MCP 安装范围 如何在wsl2环境下给claude code cli 配置 playwright-mcp wsl的网络模式有哪几种,有哪些区别? AI Agent memory是什么? ai agent skills是什么? Go测试生态系统工具与最佳实践深度调研(聚焦认证授权系统) 线性代数中常见矩阵类型的概念关系思维导图 三种主流授权策略
Java 并发编程发展史与 java.util.concurrent 全景解析
悠哉大斌 · 2026-05-25 · via 博客园 - 悠哉大斌

Java 并发编程发展史与 java.util.concurrent 全景解析

面向刚接触并发编程的开发者,一篇从历史、思想到实战的入门技术博客。


一、为什么需要并发编程?

很多初学者第一次接触并发编程时,都会有一个疑问:

“单线程不是已经能工作了吗?为什么还需要多线程?”

答案很简单:

现代软件系统越来越需要:

  • 同时处理大量请求
  • 提高 CPU 利用率
  • 充分利用多核处理器
  • 避免程序阻塞
  • 提升吞吐量和响应速度

例如:

场景 为什么需要并发
Web 服务 同时处理多个用户请求
数据库连接池 多线程复用连接
消息队列 异步消费消息
游戏服务器 同时处理多个玩家
GUI 程序 避免界面卡死
AI / 大数据 并行计算

而 Java,从诞生开始,就把并发视为语言核心能力之一。


二、Java 并发编程的发展历史

Java 并发的发展,大致可以分为六个阶段。


三、第一阶段:Thread 时代(JDK 1.0)

Java 在 1995 年发布时,就内置了:

  • Thread
  • synchronized
  • wait/notify

这是当时很多语言都没有的能力。

1. Thread 的基本使用

class MyTask extends Thread {
    @Override
    public void run() {
        System.out.println("Hello Thread");
    }
}

public class Main {
    public static void main(String[] args) {
        new MyTask().start();
    }
}

或者:

new Thread(() -> {
    System.out.println("Hello");
}).start();

2. synchronized:Java 最早的锁

public synchronized void increment() {
    count++;
}

synchronized 本质上是:

JVM 内置的互斥锁(Monitor Lock)

它可以保证:

  • 同一时间只有一个线程进入临界区
  • 保证内存可见性
  • 保证一定程度的有序性

3. 早期并发的问题

虽然 Java 很早支持多线程,但早期 API 非常原始。

开发者需要手动处理:

  • 线程创建
  • 生命周期
  • 锁竞争
  • wait/notify
  • 死锁
  • 线程通信

代码复杂且容易出错。

例如:

synchronized (lock) {
    while (!condition) {
        lock.wait();
    }

    // do something

    lock.notifyAll();
}

很多人第一次看到 wait/notify 都会怀疑人生。

因为:

  • 必须配合 synchronized
  • 必须使用 while
  • 容易丢失通知
  • 容易死锁
  • 调试困难

于是:

Java 社区开始探索更高级的并发抽象。


四、第二阶段:Java 内存模型(JMM)建立(JDK 1.2 ~ 1.5)

随着多核 CPU 出现,一个严重问题开始浮现:

多线程下,变量为什么会“莫名其妙”出错?

例如:

class Example {
    boolean ready = false;

    void writer() {
        ready = true;
    }

    void reader() {
        if (ready) {
            System.out.println("OK");
        }
    }
}

理论上:

writer() 执行后,reader() 应该看到 ready=true

但实际上:

可能看不到。

原因包括:

  • CPU 缓存
  • 指令重排序
  • 编译器优化
  • 多核缓存不一致

于是 Java 引入:

Java Memory Model(JMM)

JMM 定义了:

  • 线程之间如何共享内存
  • 什么叫“可见性”
  • 什么叫“有序性”
  • happens-before 规则

volatile 的出现

volatile boolean ready;

volatile 保证:

  • 一个线程修改后,其他线程立刻可见
  • 禁止部分重排序

但它:

  • 不保证原子性
  • 不能替代锁

例如:

volatile int count;
count++;

仍然不是线程安全的。


五、第三阶段:java.util.concurrent(JDK 1.5)革命

这是 Java 并发历史上最重要的一次升级。

2004 年,JDK 1.5 发布。

Doug Lea 主导设计了:

java.util.concurrent

简称:

JUC

它彻底改变了 Java 并发编程。


六、JUC 的核心设计思想

JUC 最大的贡献是:

用“高层抽象”替代底层线程操作。

也就是说:

开发者不再需要:

  • 手写 wait/notify
  • 手动管理线程
  • 自己实现线程池
  • 自己实现锁
  • 自己实现队列

而是直接使用成熟组件。

这和现代 Python / Go / Rust 的并发思想非常接近。


七、JUC 包全景图

JUC 可以分成几个核心模块:

java.util.concurrent
├── Executor(线程池)
├── Locks(锁)
├── Atomic(原子类)
├── Collections(并发集合)
├── Synchronizers(同步器)
├── ForkJoin(分治并行)
├── Future / CompletableFuture(异步编程)
└── Flow(响应式流)

下面逐个解析。


八、Executor:线程池革命

这是 JUC 最重要的模块之一。


1. 为什么需要线程池?

早期代码:

new Thread(task).start();

问题:

  • 线程创建成本高
  • 频繁创建/销毁浪费资源
  • 无法限制线程数量
  • 容易 OOM

线程池的思想:

线程复用。


2. Executor 框架

ExecutorService pool = Executors.newFixedThreadPool(4);

pool.submit(() -> {
    System.out.println(Thread.currentThread().getName());
});

核心接口:

接口 作用
Executor 执行任务
ExecutorService 管理线程池
ScheduledExecutorService 定时任务

3. ThreadPoolExecutor(核心中的核心)

实际上:

Executors.newFixedThreadPool()

底层就是:

ThreadPoolExecutor

其核心参数:

ThreadPoolExecutor(
    corePoolSize,
    maximumPoolSize,
    keepAliveTime,
    unit,
    workQueue,
    threadFactory,
    handler
)

初学者重点理解:

参数 含义
corePoolSize 核心线程数
maximumPoolSize 最大线程数
workQueue 任务队列
handler 拒绝策略

4. 线程池执行流程

提交任务
   ↓
核心线程满了吗?
   ↓
否 → 创建核心线程
是
   ↓
队列满了吗?
   ↓
否 → 进入队列
是
   ↓
达到最大线程数了吗?
   ↓
否 → 创建临时线程
是
   ↓
触发拒绝策略

5. Executors 为什么经常不推荐?

很多教程会这样写:

Executors.newFixedThreadPool(100)

但阿里 Java 规范不推荐。

因为:

默认队列可能无界。

可能导致:

  • 内存暴涨
  • OOM
  • 请求堆积

生产环境通常手动创建:

new ThreadPoolExecutor(...)

九、Locks:显式锁体系

虽然 synchronized 很方便。

但它功能有限。

于是 JUC 引入:

java.util.concurrent.locks

1. ReentrantLock

Lock lock = new ReentrantLock();

lock.lock();
try {
    // critical section
} finally {
    lock.unlock();
}

相比 synchronized:

特性 synchronized ReentrantLock
自动释放锁
可中断
超时获取锁
公平锁
条件变量

2. Condition

Condition 类似:

wait / notify 的升级版
Condition condition = lock.newCondition();

condition.await();
condition.signal();

它比 wait/notify 更清晰。


3. ReadWriteLock

思想:

读共享,写互斥
ReadWriteLock rwLock = new ReentrantReadWriteLock();

适合:

  • 读多写少
  • 缓存系统
  • 配置中心

4. StampedLock(JDK 8)

进一步优化读性能。

支持:

  • 乐观读
  • 悲观读
  • 写锁

适合高并发读场景。


十、Atomic:无锁并发(CAS)

这是很多人第一次接触“无锁编程”。


1. AtomicInteger

AtomicInteger counter = new AtomicInteger();

counter.incrementAndGet();

为什么它线程安全?

因为底层使用:

CAS(Compare And Swap)

思想:

如果当前值还是预期值
那么就更新
否则重试

2. CAS 的优点

相比锁:

  • 不阻塞线程
  • 性能高
  • 避免线程切换

3. CAS 的问题

ABA 问题

A → B → A

值虽然没变。

但过程变了。

解决方案:

AtomicStampedReference

4. LongAdder(高并发优化)

LongAdder adder = new LongAdder();

高并发下比 AtomicLong 更快。

思想:

分段累加
减少竞争

这是现代高性能计数器经典设计。


十一、并发集合

普通集合:

ArrayList
HashMap

都不是线程安全的。


1. ConcurrentHashMap

JUC 最经典组件之一。

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

它经历了两代设计:

JDK 实现
JDK 7 Segment 分段锁
JDK 8 CAS + synchronized

2. CopyOnWriteArrayList

思想:

写时复制

写操作:

  • 复制新数组
  • 修改新数组
  • 替换旧数组

适合:

  • 读远多于写
  • 监听器列表
  • 配置快照

3. BlockingQueue

这是生产者消费者模型核心。

BlockingQueue<String> queue = new LinkedBlockingQueue<>();

常见实现:

特点
ArrayBlockingQueue 有界数组队列
LinkedBlockingQueue 链表队列
PriorityBlockingQueue 优先级队列
DelayQueue 延迟队列
SynchronousQueue 不存储元素

很多线程池底层都依赖 BlockingQueue。


十二、Synchronizers:同步器体系

这是 JUC 非常强大的设计。


1. CountDownLatch

CountDownLatch latch = new CountDownLatch(3);

作用:

等待多个任务完成

例如:

  • 等待多个服务启动
  • 等待多个线程结束

2. CyclicBarrier

让多个线程互相等待

类似:

“大家到齐再出发”

3. Semaphore

Semaphore semaphore = new Semaphore(3);

作用:

限制并发数量

例如:

  • 数据库连接池
  • 限流
  • 资源池

4. Phaser

JDK 7 引入。

更灵活的阶段同步器。

适合复杂并行流程。


十三、Fork/Join:分治并行(JDK 7)

现代 CPU 已经是多核。

于是 Java 引入:

ForkJoinPool

核心思想:

分而治之

例如:

大任务
 → 拆分小任务
 → 并行执行
 → 合并结果

Work Stealing(工作窃取)

这是 ForkJoin 最大创新。

空闲线程
去偷其他线程任务

提升 CPU 利用率。


示例

class SumTask extends RecursiveTask<Long> {
    // compute()
}

后来:

Java Stream 并行流底层也基于 ForkJoinPool。

list.parallelStream()

十四、Future 与 CompletableFuture

这是 Java 异步编程演进史。


1. Future(JDK 5)

Future<Integer> future = pool.submit(() -> 1 + 2);

Integer result = future.get();

问题:

get() 会阻塞

无法优雅组合异步任务。


2. CompletableFuture(JDK 8)

革命性升级。

CompletableFuture
    .supplyAsync(() -> "Hello")
    .thenApply(s -> s + " World")
    .thenAccept(System.out::println);

它支持:

  • 回调
  • 链式调用
  • 异常处理
  • 组合任务
  • 并行任务

类似 JavaScript Promise

很多前端开发者会发现:

CompletableFuture 很像 Promise

确实如此。

例如:

Java JavaScript
thenApply then
exceptionally catch
allOf Promise.all

十五、Flow:响应式流(JDK 9)

JDK 9 引入:

java.util.concurrent.Flow

实现 Reactive Streams 标准。

核心思想:

异步数据流
+ 背压(Backpressure)

适合:

  • 流式处理
  • 消息系统
  • 响应式编程

不过:

实际生产中更常见的是:

  • Reactor
  • RxJava
  • Spring WebFlux

十六、虚拟线程(Project Loom,JDK 21)

这是近年来 Java 并发最大变革。


1. 为什么传统线程昂贵?

传统线程:

1 个线程 ≈ 1 个操作系统线程

成本很高。

大量线程会:

  • 内存占用大
  • 上下文切换昂贵
  • 调度开销高

2. 虚拟线程

JDK 21 正式引入:

Thread.startVirtualThread(() -> {
    System.out.println("Hello Virtual Thread");
});

思想类似:

  • Go goroutine
  • Kotlin coroutine
  • Erlang process

特点:

  • 极轻量
  • 数十万线程可行
  • 阻塞代码也能高并发

3. 为什么意义巨大?

过去 Java 高并发依赖:

异步回调

代码复杂。

虚拟线程让开发者:

重新写“同步代码”
却获得高并发能力

这可能改变未来 Java 服务端编程方式。


十七、并发编程核心问题

学习并发,最重要的是理解下面几个概念。


1. 原子性(Atomicity)

操作不可分割。

例如:

count++

实际上不是原子操作。

它包括:

读取
修改
写回

2. 可见性(Visibility)

一个线程修改变量。

另一个线程能否立即看到?

这就是:

volatile
锁
JMM

解决的问题。


3. 有序性(Ordering)

CPU 和编译器可能重排序。

导致多线程问题。


4. 死锁(Deadlock)

典型情况:

线程 A 等 B
线程 B 等 A

避免方法:

  • 固定加锁顺序
  • 减少锁嵌套
  • 超时锁

十八、JUC 的底层基石:AQS

很多 JUC 组件底层都基于:

AbstractQueuedSynchronizer

简称:

AQS

例如:

  • ReentrantLock
  • Semaphore
  • CountDownLatch
  • ReentrantReadWriteLock

都基于 AQS。


AQS 核心思想

state + CLH 队列 + CAS

即:

  • 一个 state 表示同步状态
  • 获取失败进入队列
  • CAS 修改状态
  • park/unpark 阻塞唤醒线程

AQS 可以说是:

Java 并发框架的“发动机”

十九、现代 Java 并发推荐学习路线

很多初学者一上来:

  • 死磕源码
  • 死磕 AQS
  • 死磕 Unsafe

结果很快劝退。

正确顺序应该是:


第一阶段:理解并发基础

重点:

  • 线程是什么
  • 进程 vs 线程
  • synchronized
  • volatile
  • happens-before

推荐目标:

能理解:

为什么多线程会出错

第二阶段:掌握 JUC 常用组件

重点:

  • ThreadPoolExecutor
  • ConcurrentHashMap
  • ReentrantLock
  • BlockingQueue
  • CompletableFuture

推荐目标:

能够编写实际业务代码。


第三阶段:理解底层原理

重点:

  • CAS
  • AQS
  • park/unpark
  • CPU 缓存一致性
  • 内存屏障

第四阶段:高性能并发设计

重点:

  • 无锁编程
  • Disruptor
  • Actor 模型
  • Reactive
  • 虚拟线程

二十、初学者最容易踩的坑


1. 滥用 synchronized

锁范围过大。

导致性能下降。


2. 线程池不关闭

pool.shutdown();

很多人忘记。


3. 使用 Executors 默认线程池

可能导致 OOM。


4. 并发修改普通 HashMap

可能死循环。

应使用:

ConcurrentHashMap

5. 误以为 volatile 能解决一切

它不保证复合操作原子性。


二十一、现代 Java 并发生态

除了 JUC,现代 Java 并发还有很多重要框架。

技术 作用
Netty 高性能网络框架
Reactor 响应式编程
RxJava Reactive 扩展
Akka Actor 模型
Quasar 早期协程方案
Loom 虚拟线程

二十二、Java 并发思想对其他语言的影响

Java 的很多思想后来影响了整个行业。

例如:

Java 后来的对应思想
Future Promise
CompletableFuture async pipeline
ForkJoin work stealing
ConcurrentHashMap lock striping
Loom goroutine 风格

Java 并发生态其实非常超前。


二十三、给初学者的建议

如果你刚开始学习并发:

不要一开始就研究:

  • JVM 汇编
  • Unsafe
  • LockSupport
  • AQS 源码

先学会:

如何正确使用并发工具

这是最重要的。

真正的成长路径是:

会用 → 理解原理 → 理解设计 → 能自己设计并发系统

二十四、总结

Java 并发的发展史,本质上是:

从“操作线程”
到
“抽象并发”
再到
“简化并发”

的发展过程。

它经历了:

阶段 核心特征
Thread 时代 手动线程管理
synchronized 时代 JVM 内置锁
JUC 时代 高级并发组件
ForkJoin 时代 多核并行
CompletableFuture 时代 异步编排
Loom 时代 轻量线程

java.util.concurrent

则是 Java 并发体系真正成熟的标志。

它不仅提供工具。

更重要的是:

它定义了现代 Java 并发编程的方法论。


二十五、推荐阅读

官方资料

  • Java Concurrency in Practice
  • JSR-133(Java Memory Model)
  • Doug Lea 的并发论文

推荐源码阅读顺序

  1. ThreadPoolExecutor
  2. ConcurrentHashMap
  3. ReentrantLock
  4. AQS
  5. ForkJoinPool

结语

并发编程并不是 Java 独有的话题。

但 Java 是整个工业界里:

对并发体系建设最完整、最系统的语言之一。

理解 Java 并发,实际上也是在理解:

  • 操作系统
  • CPU
  • 内存模型
  • 调度系统
  • 高性能服务架构

这也是为什么:

很多高级程序员最终都会回到并发原理本身。