1. 为什么选择一周速通Java并发编程?
Java并发编程一直是开发者进阶路上的关键门槛。我见过太多同行在这个领域栽跟头——要么被死锁问题折磨得夜不能寐,要么在多线程调试中怀疑人生。但现实是,现代应用开发几乎离不开并发编程,从电商秒杀到实时数据处理,都需要扎实的并发功底。
我设计这个"一周速通"计划,源于自己当年面试时被ThreadPoolExecutor参数问懵的惨痛经历。经过多年实战,我发现只要抓住几个核心概念,配合正确的练习方法,完全可以在短时间内建立系统的并发知识框架。不同于传统的"从入门到精通"路线,这个计划更注重实用性和快速上手,特别适合:
- 准备跳槽急需补充并发知识的开发者
- 工作中突然需要处理多线程任务的程序员
- 想要系统性梳理并发知识体系的Java工程师
2. 七日学习路线设计
2.1 第一天:线程基础与生命周期
我们从最基础的Thread类开始。很多教程一上来就讲如何创建线程,但更重要的是理解线程的6种状态:
java复制public enum State {
NEW, // 新建但未启动
RUNNABLE, // 可运行(可能在运行也可能在等待CPU)
BLOCKED, // 等待监视器锁
WAITING, // 无限期等待
TIMED_WAITING, // 有限期等待
TERMINATED; // 终止
}
实操建议:用jstack工具观察线程状态变化。我常用这个命令查看生产环境的线程阻塞情况:
bash复制jstack -l <pid> > thread_dump.log
注意:直接继承Thread类的方式在实际项目中很少使用,更推荐实现Runnable接口。因为Java不支持多重继承,这种方式更灵活。
2.2 第二天:锁机制深度解析
synchronized关键字是大多数Java开发者接触的第一个锁机制,但它的实现原理常被误解。在JDK1.6之后,synchronized经历了重大优化,引入了锁升级机制:
无锁 → 偏向锁 → 轻量级锁 → 重量级锁
通过这个JVM参数可以查看锁升级过程:
bash复制-XX:+PrintFlagsFinal | grep BiasedLocking
ReentrantLock相比synchronized提供了更灵活的特性,比如可中断、公平锁、多条件变量。但在实际项目中,我建议:
- 简单同步场景用synchronized(JVM会自动优化)
- 需要高级功能时用ReentrantLock
- 读写分离场景用ReadWriteLock
2.3 第三天:线程池实战技巧
ThreadPoolExecutor的7个核心参数需要牢记:
- corePoolSize - 核心线程数
- maximumPoolSize - 最大线程数
- keepAliveTime - 空闲线程存活时间
- unit - 时间单位
- workQueue - 任务队列
- threadFactory - 线程工厂
- handler - 拒绝策略
我常用的线程池配置原则:
- CPU密集型任务:线程数 = CPU核心数 + 1
- IO密集型任务:线程数 = CPU核心数 * (1 + 平均等待时间/平均计算时间)
踩坑记录:曾经因为workQueue使用无界队列导致OOM,后来改用ArrayBlockingQueue并设置合理大小。
2.4 第四天:并发集合类应用
ConcurrentHashMap在JDK8中的重大改进:
- 抛弃分段锁,改用CAS+synchronized
- 引入红黑树优化哈希冲突
- size()方法不再准确(为了性能牺牲一致性)
CopyOnWriteArrayList适合读多写少的场景,但要注意:
- 每次修改都会复制整个数组,写性能差
- 迭代器遍历的是创建时的快照
我常用的并发工具类选择策略:
- 高频更新少量查询:Collections.synchronizedList
- 低频更新高频查询:CopyOnWriteArrayList
- 键值对存储:ConcurrentHashMap
2.5 第五天:原子类与CAS原理
AtomicInteger等原子类的底层是CAS(Compare And Swap)操作。现代CPU通过LOCK CMPXCHG指令实现原子性。
ABA问题是CAS的典型陷阱,解决方案:
- 加版本号(AtomicStampedReference)
- 加时间戳
我参与的电商项目中,商品库存扣减就采用了AtomicInteger:
java复制public boolean deductStock(int num) {
int current;
do {
current = stock.get();
if (current < num) {
return false;
}
} while (!stock.compareAndSet(current, current - num));
return true;
}
2.6 第六天:并发设计模式
生产者-消费者模式的三种实现方式:
- wait/notify
- Lock/Condition
- BlockingQueue(推荐)
我在日志收集系统中使用LinkedBlockingQueue实现生产者消费者:
java复制BlockingQueue<LogEntry> queue = new LinkedBlockingQueue<>(1000);
// 生产者
public void produce(LogEntry log) throws InterruptedException {
queue.put(log); // 队列满时阻塞
}
// 消费者
public LogEntry consume() throws InterruptedException {
return queue.take(); // 队列空时阻塞
}
2.7 第七天:性能调优与问题排查
常见的并发性能问题:
- 锁竞争激烈(用JProfiler查看锁等待时间)
- 上下文切换频繁(vmstat查看cs值)
- 内存可见性问题(volatile或同步块解决)
我总结的死锁排查四步法:
- jstack获取线程dump
- 查找"BLOCKED"状态线程
- 分析锁持有和等待关系
- 检查是否存在循环等待
3. 高频面试题精讲
3.1 volatile关键字
volatile保证可见性和有序性,但不保证原子性。它的底层实现:
- 写操作:JVM插入StoreStore和StoreLoad内存屏障
- 读操作:插入LoadLoad和LoadStore内存屏障
适用场景:
- 状态标志位(如shutdownRequested)
- 单例模式的双重检查锁定
3.2 ThreadLocal原理
每个Thread内部有ThreadLocalMap,key是弱引用的ThreadLocal实例。常见内存泄漏场景:
- 线程池复用线程导致ThreadLocal积累
- ThreadLocal被回收但value还在
正确用法:
java复制try {
threadLocal.set(value);
// 业务逻辑
} finally {
threadLocal.remove(); // 必须清理
}
3.3 AQS框架解析
AbstractQueuedSynchronizer是并发包的核心基础,ReentrantLock、CountDownLatch等都基于它实现。核心数据结构:
- state:同步状态
- CLH队列:等待线程的FIFO队列
自定义同步器示例:
java复制class Mutex extends AbstractQueuedSynchronizer {
protected boolean tryAcquire(int acquires) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int releases) {
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
}
4. 实战项目建议
4.1 简易秒杀系统
核心挑战:超卖问题解决方案
- 乐观锁(版本号)
- Redis原子操作
- 分布式锁(Redisson)
我实现的版本采用Redis+Lua脚本:
lua复制local stock = tonumber(redis.call('GET', KEYS[1]))
if stock > 0 then
redis.call('DECR', KEYS[1])
return 1
end
return 0
4.2 多线程日志收集器
设计要点:
- 异步写入避免阻塞业务线程
- 批量提交减少IO次数
- 双缓冲减少锁竞争
性能对比:
| 方案 | QPS | 平均延迟 |
|---|---|---|
| 同步写入 | 1200 | 15ms |
| 异步单缓冲 | 8500 | 2ms |
| 异步双缓冲 | 9200 | 1ms |
5. 学习资源推荐
5.1 必读书籍
- 《Java并发编程实战》(理论基础)
- 《Java并发编程之美》(实战案例)
- 《深入理解Java虚拟机》(底层原理)
5.2 在线工具
- JVisualVM:监控线程状态
- JCStress:并发测试工具
- Java Concurrency Stress Tests:压力测试套件
5.3 练习平台
- LeetCode多线程题目
- Codewars并发挑战
- 自己实现小型线程池
我在实际教学中发现,很多开发者对并发存在畏惧心理。但通过这个结构化学习路径,配合每日的代码实操,大多数人都能在7天内建立起系统的并发知识框架。关键是要动手实践——光看理论是永远掌握不了并发编程的。
