并发
Java并发编程核心知识点整理
一、线程中断机制(interrupt)
为什么interrupt()可以中断线程?
interrupt()方法并不是强制中断线程,而是通过设置中断标志的方式,由线程自己决定如何处理中断。
两种中断场景:
-
线程处于阻塞状态(如
sleep()、wait()、join())- 调用
interrupt()会立即抛出InterruptedException异常,从而中断线程
- 调用
-
线程正在运行状态
interrupt()仅设置中断标志位(true)- 需要线程主动检查中断状态(如
isInterrupted()),自行决定是否终止执行
二、线程池工作原理
任务执行流程:
- 检查核心线程数是否已满
- 未满 → 创建核心线程执行任务
- 已满 → 进入步骤2
- 检查工作队列是否已满
- 未满 → 将任务放入队列等待
- 已满 → 进入步骤3
- 检查当前线程数是否超过最大线程数
- 未超过 → 创建非核心线程执行任务
- 超过 → 执行拒绝策略
线程池核心参数:
| 参数 | 说明 |
|---|---|
| 核心线程数 | 常驻的核心线程数量 |
| 最大线程数 | 核心线程数 + 非核心线程数 |
| 工作队列 | 存放等待执行的任务 |
| 拒绝策略 | 任务无法执行时的处理方式 |
最佳实践:
手动创建线程池,根据业务场景合理配置参数,避免使用Executors默认实现(可能引发OOM)。
三、CountDownLatch详解
作用:
允许一个或多个线程等待其他线程完成操作后再继续执行。
实现原理:
- 通过计数器实现
await():使当前线程等待,直到计数器为0countDown():计数器减1- 计数器减至0后不会重置
四、单例模式中的volatile
问题:
为什么使用了synchronized还需要volatile?
原因:
防止指令重排序
对象创建三步过程:
- 分配内存空间
- 初始化对象
- 将内存地址赋值给引用
风险:
如果没有volatile,可能发生指令重排(2和3顺序颠倒),导致其他线程获取到未初始化完成的对象。
五、sleep() vs wait()
| 对比项 | sleep() | wait() |
|---|---|---|
| 锁释放 | ❌ 不释放锁 | ✅ 释放锁 |
| 所属类 | Thread类 | Object类 |
| 唤醒方式 | 时间到自动唤醒 | 需notify()/notifyAll() |
六、CAS与ABA问题
CAS原理:
比较并交换,包含三个值:
- 当前内存值 V
- 期望值 E
- 新值 N
当 V == E 时,将 V 更新为 N
ABA问题:
线程1:A → B → A
线程2:观察到值仍为A,认为未被修改,继续操作
解决方案:
版本号机制(如AtomicStampedReference)
同时检查值和版本号,确保变量未被修改过。
七、AQS核心思想
AbstractQueuedSynchronizer(AQS)原理:
- 资源空闲:将当前线程设为有效工作线程,锁定资源
- 资源被占用:将线程放入FIFO双向队列
- 通过自旋机制等待被唤醒
八、并发工具对比
| 工具类 | 作用 |
|---|---|
| CountDownLatch | 一个/多个线程等待其他线程完成 |
| CyclicBarrier | 多个线程互相等待,到达屏障点后继续执行 |
| ThreadLocal | 每个线程拥有独立变量副本,避免线程间同步消耗 |
九、synchronized锁升级过程
锁的四种状态(由低到高):
-
无锁状态
- 没有线程获取锁
-
偏向锁
- 只有一个线程获取锁
- 性能优于正常锁
-
轻量级锁
- 多个线程通过CAS自旋竞争锁
-
重量级锁
- CAS自旋超过10次(或自旋线程数超过CPU核心数一半)
- 升级为重量级锁,进入操作系统内核态阻塞
总结
理解Java并发编程的核心机制,对于编写高性能、线程安全的程序至关重要。本文总结了线程中断、线程池、锁机制、并发工具等关键知识点,希望对您的学习和工作有所帮助。

浙公网安备 33010602011771号