Java 并发编程完全指南:从基础概念到实战优化
·
一、并发编程基础概念
1.1 进程与线程
- 定义与区别:进程是资源分配单位(内存 / 文件句柄),线程是 CPU 调度单位(共享进程资源),Java 线程实现(基于内核线程 1:1 模型)
- 线程状态:新建(NEW)→就绪(RUNNABLE)→运行(RUNNING)→阻塞(BLOCKED/WAITING/TIMED_WAITING)→死亡(TERMINATED)
- 上下文切换:CPU 从一个线程切换到另一个线程的开销(保存 / 恢复寄存器、缓存失效),平均耗时约 1-10 微秒
1.2 并发与并行
- 并发:同一时间段内多个任务交替执行(单核 CPU 通过时间片轮转实现)
- 并行:同一时刻多个任务同时执行(多核 CPU 真正并行处理)
- Java 支持:通过 Thread 类、线程池、Fork/Join 框架实现并发 / 并行编程
1.3 线程安全三要素
- 原子性:操作不可中断(如 i++ 不是原子操作,需同步保证)
- 可见性:一个线程修改的共享变量对其他线程立即可见(volatile 关键字保证)
- 有序性:指令执行顺序不被重排序(happens-before 规则约束)
二、线程创建与管理
2.1 线程创建方式
- 继承 Thread 类:重写 run () 方法,单继承限制
- 实现 Runnable 接口:无返回值,适合多线程共享资源
- 实现 Callable 接口:通过 FutureTask 获取返回值,支持异常抛出
- 对比:Callable 可获取返回值,Runnable 更轻量,Thread 扩展性差
2.2 线程池核心原理
- 核心参数:
- 核心线程数(corePoolSize):保持活跃的最小线程数
- 最大线程数(maximumPoolSize):允许创建的最大线程数
- 队列容量(workQueue):任务等待队列(ArrayBlockingQueue/LinkedBlockingQueue)
- 拒绝策略(RejectedExecutionHandler):AbortPolicy/DiscardPolicy/CallerRunsPolicy
- 工作流程:提交任务→核心线程处理→队列缓冲→扩容至最大线程→执行拒绝策略
2.3 线程池类型与适用场景
- FixedThreadPool:固定线程数,适用于任务量稳定场景
- CachedThreadPool:动态扩容线程,适用于短期突发任务
- ScheduledThreadPool:定时 / 周期性任务,适用于调度场景
- SingleThreadExecutor:单线程串行执行,适用于需要顺序执行的任务
三、同步机制详解
3.1 synchronized 关键字
- 作用范围:
- 实例方法:对象锁(锁当前对象实例)
- 静态方法:类锁(锁类的 Class 对象)
- 代码块:指定锁对象(synchronized (obj))
- 锁优化:
- 偏向锁:无竞争时直接获取锁标记
- 轻量级锁:CAS 尝试获取锁,避免内核态切换
- 重量级锁:竞争激烈时膨胀为互斥锁,依赖操作系统 Mutex
- 使用注意:避免锁升级、减少锁持有时间、避免嵌套锁
3.2 volatile 关键字
- 内存可见性:强制线程从主内存读取变量,而非工作内存缓存
- 禁止重排序:通过内存屏障保证指令执行顺序
- 使用场景:状态标记(如 boolean running)、单例模式双重检查
- 局限性:不保证原子性,不能替代 synchronized
3.3 Lock 接口实现类
- ReentrantLock:可中断锁、超时获取锁、公平锁选项,性能优于 synchronized(高并发场景)
- ReadWriteLock:读写分离锁,读操作共享,写操作互斥,提升读多写少场景性能
- StampedLock:乐观读模式,无竞争时读操作无需加锁,进一步提升并发读性能
- Condition 接口:await ()/signal () 实现线程间通信,支持多个条件变量
四、并发容器
4.1 ConcurrentHashMap
- JDK 7 实现:分段锁(Segment 数组),每个 Segment 独立加锁
- JDK 8 优化:取消分段锁,采用 CAS+ synchronized 实现,Node 数组 + 红黑树结构
- 核心方法:putVal ()(CAS + 自旋)、get ()(无锁读取)、size ()(通过 baseCount+counterCells 计算)
- 性能对比:并发写入性能是 Hashtable 的 5 倍,接近 HashMap(非线程安全)
4.2 阻塞队列
- ArrayBlockingQueue:有界数组实现,FIFO 顺序,公平 / 非公平锁选项
- LinkedBlockingQueue:无界链表实现(默认 Integer.MAX_VALUE),吞吐量高于 ArrayBlockingQueue
- SynchronousQueue:无缓冲队列,直接移交任务,适用于任务快速处理场景
- PriorityBlockingQueue:优先级排序队列,元素需实现 Comparable 接口
4.3 其他并发容器
- CopyOnWriteArrayList:写时复制容器,读操作无锁,适用于读多写少场景
- ConcurrentLinkedQueue:无锁队列,CAS 操作实现入队 / 出队,高并发场景性能优异
- ConcurrentSkipListMap:跳表实现,支持排序,并发环境替代 TreeMap
- BlockingDeque:双端阻塞队列,支持从两端插入 / 移除元素(LinkedBlockingDeque)
五、线程协作工具
5.1 wait/notify 机制
- 使用条件:必须在 synchronized 块中调用,否则抛出 IllegalMonitorStateException
- 工作流程:wait () 释放锁并进入等待集→notify () 唤醒单个线程→notifyAll () 唤醒所有线程
- 注意事项:避免虚假唤醒(需在循环中检查条件),notify () 随机唤醒可能导致信号丢失
5.2 CountDownLatch 与 CyclicBarrier
- CountDownLatch:
- 功能:等待多个线程完成后再执行(如主线程等待子线程初始化)
- 实现:通过 countDown () 递减计数器,await () 阻塞等待计数器为 0
- 特点:计数器不可重置,一次性使用
- CyclicBarrier:
- 功能:多个线程到达屏障后再继续执行(如多线程分阶段任务)
- 实现:通过 await () 等待,所有线程到达后触发 Runnable 任务
- 特点:计数器可重置(reset ()),循环使用
5.3 Semaphore 与 Exchanger
- Semaphore:
- 功能:控制并发访问资源的线程数(如连接池限流)
- 操作:acquire () 获取许可,release () 释放许可
- 应用:数据库连接池、接口限流
- Exchanger:
- 功能:两个线程交换数据(如生产者消费者交换数据)
- 操作:exchange () 等待对方线程到达,交换数据后返回
- 应用:数据校对、遗传算法并行计算
六、线程安全设计模式
6.1 不可变对象模式
- 实现方式:所有字段声明为 final,无 setter 方法,返回新对象而非修改原有对象
- 优势:天生线程安全,无需同步操作
- 案例:String 类、Integer 等包装类、Google Guava 的 Immutable 系列
6.2 线程封闭模式
- ThreadLocal:每个线程独立存储变量副本,避免共享
- 实现原理:Thread 类中的 ThreadLocalMap 存储线程私有变量
- 使用场景:数据库连接管理、Session 管理、多线程上下文传递
- 注意事项:可能导致内存泄漏(需显式 remove ())
6.3 原子类与 CAS 操作
- 原子更新基本类型:AtomicInteger/AtomicLong/AtomicBoolean
- 原子更新数组:AtomicIntegerArray/AtomicLongArray
- 原子更新引用:AtomicReference/AtomicStampedReference(解决 ABA 问题)
- CAS 操作:Compare-And-Swap,无锁编程核心(Unsafe 类实现)
七、高级并发特性
7.1 CompletableFuture 异步编程
- 核心方法:
- supplyAsync ():异步执行有返回值任务
- thenApply ()/thenAccept ()/thenRun ():任务完成后链式处理
- exceptionally ():异常处理
- allOf ()/anyOf ():等待多个任务完成
- 优势:简化异步代码,避免回调地狱,支持函数式编程
7.2 Fork/Join 框架
- 分治思想:将大任务拆分为小任务并行处理,合并结果
- 核心类:
- ForkJoinPool:专门处理 ForkJoinTask 的线程池
- RecursiveTask:有返回值的任务
- RecursiveAction:无返回值的任务
- 工作窃取:空闲线程窃取其他线程队列尾部任务,提高 CPU 利用率
7.3 虚拟线程(JDK 21 新特性)
- 轻量级线程:由 JVM 管理,而非 OS 内核线程,降低上下文切换开销
- 创建方式:Thread.startVirtualThread()、ExecutorService.newVirtualThreadPerTaskExecutor()
- 优势:支持百万级并发,适合 I/O 密集型任务(如网络请求、文件读写)
- 局限性:不适合 CPU 密集型任务,不能替代传统线程
八、并发性能优化
8.1 线程池参数调优
- 核心线程数配置:
- CPU 密集型任务:核心线程数 = CPU 核心数 + 1
- I/O 密集型任务:核心线程数 = CPU 核心数 * 2
- 队列选择:
- 任务量已知:ArrayBlockingQueue(有界,避免内存溢出)
- 任务量未知:LinkedBlockingQueue(无界,需控制最大线程数)
- 拒绝策略:
- 核心业务:AbortPolicy(抛出异常,及时发现问题)
- 非核心业务:DiscardOldestPolicy(丢弃 oldest 任务)
8.2 锁优化策略
- 减少锁粒度:将大锁拆分为小锁(如 ConcurrentHashMap 的分段锁)
- 读写分离:使用 ReadWriteLock/StampedLock,读操作共享
- 锁粗化:合并多次连续加锁,减少锁获取释放次数
- 无锁编程:使用 CAS 操作、原子类替代锁
8.3 监控与诊断
- 工具使用:
- JConsole:线程状态监控、内存使用分析
- VisualVM:线程 dump 分析、CPU / 内存采样
- Arthas:在线诊断线程问题(thread 命令查看线程状态)
- 性能指标:
- 线程数:避免创建过多线程导致 OOM
- 锁等待时间:通过 jstack 分析 BLOCKED 线程
- 任务执行时间:监控线程池任务耗时
九、实战案例分析
9.1 生产消费者模型
- 基于 BlockingQueue 实现:
java
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10); // 生产者 new Thread(() -> { for (int i = 0; i < 100; i++) { queue.put(i); // 满时阻塞 } }).start(); // 消费者 new Thread(() -> { while (true) { Integer task = queue.take(); // 空时阻塞 process(task); } }).start();
9.2 分布式锁实现
- Redis 分布式锁:
java
String lockKey = "order:123"; String requestId = UUID.randomUUID().toString(); boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, 30, TimeUnit.SECONDS); if (locked) { try { // 执行业务逻辑 } finally { // 释放锁(判断requestId防止误删) String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; redisTemplate.execute(new DefaultRedisScript<>(script, Integer.class), Collections.singletonList(lockKey), requestId); } }
9.3 高并发秒杀系统
- 库存并发控制:
- 悲观锁:select ... for update 锁定库存记录
- 乐观锁:版本号 / 时间戳控制(update stock set count=count-1 where id=1 and count>0)
- 原子类:使用 AtomicInteger/LongAdder 实现库存扣减
- 限流措施:
- 前端限流:按钮置灰、验证码
- 后端限流:Semaphore 控制并发请求数,Redis+Lua 脚本实现分布式限流
十、常见问题与解决方案
10.1 死锁
- 产生条件:互斥、请求与保持、不可剥夺、循环等待
- 检测工具:jstack 命令查看 BLOCKED 线程,VisualVM 死锁检测
- 预防措施:固定锁获取顺序、使用 tryLock () 设置超时、避免嵌套锁
10.2 线程泄漏
- 原因:线程池未关闭、ThreadLocal 未 remove ()、任务执行异常未捕获
- 排查:通过 jstack 查看线程状态,监控线程数变化
- 解决:使用 try-finally 确保资源释放,线程池使用后 shutdown ()
10.3 并发容器选型
- 读多写少:CopyOnWriteArrayList/CopyOnWriteArraySet
- 高并发读写:ConcurrentHashMap/ConcurrentLinkedQueue
- 阻塞场景:ArrayBlockingQueue/LinkedBlockingQueue
- 排序需求:ConcurrentSkipListMap/ConcurrentSkipListSet
十一、学习资源与进阶方向
11.1 推荐书籍
- 《Java 并发编程实战》:并发编程圣经,深入讲解线程安全
- 《Java 并发编程的艺术》:结合实例讲解并发工具使用
- 《Java 并发编程实战》:JDK 并发包源码分析
11.2 进阶方向
- 响应式编程:Reactor/RxJava,异步非阻塞编程
- 分布式并发:分布式锁、分布式事务、一致性算法(Paxos/Raft)
- JDK 新特性:虚拟线程、结构化并发(Project Loom)
11.3 社区与工具
- 社区:Stack Overflow 并发标签、Java 并发编程论坛
- 工具:JMH 基准测试、并发编程插件(IntelliJ IDEA 的 Concurrent Tools)
附录
- Java 并发编程常用类:Thread/ThreadPoolExecutor/Lock/ConcurrentHashMap
- JVM 参数:-XX:ParallelGCThreads -XX:ConcGCThreads
- 并发编程面试题:线程池参数调优、synchronized 与 Lock 区别、CAS 原理
更多推荐



所有评论(0)