《Java 并发编程试题及答案:线程安全、锁机制、线程池考点全解析》
·
Java 并发编程核心解析:线程安全、锁与线程池实战
一、线程安全本质与解决方案
线程安全的核心矛盾:多线程对共享资源的无序访问导致数据不一致。数学描述为:
$$ \text{最终结果} \neq \text{顺序执行预期结果} $$
经典案例:原子性缺失
// 线程不安全示例
public class UnsafeCounter {
private int count = 0;
public void increment() {
count++; // 实际包含3个操作:读取→修改→写入
}
}
当两个线程同时执行increment()时,可能出现: $$ T_1(\text{读值}0) \rightarrow T_2(\text{读值}0) \rightarrow T_1(\text{写入}1) \rightarrow T_2(\text{写入}1) $$ 最终结果$count=1$而非预期值$2$
解决方案:
- 同步代码块(隐式锁)
public synchronized void increment() {
count++;
}
- 原子类(CAS实现)
private AtomicInteger atomicCount = new AtomicInteger(0);
public void safeIncrement() {
atomicCount.incrementAndGet();
}
二、锁机制深度剖析
锁类型对比
| 锁类型 | 实现方式 | 特点 |
|---|---|---|
| 隐式锁 | synchronized |
JVM自动管理锁获取/释放,支持重入 |
| 显式锁 | ReentrantLock |
可中断、超时获取、公平锁策略 |
| 读写锁 | ReentrantReadWriteLock |
读读不互斥,读写/写写互斥 |
锁优化机制
graph LR
A[无锁状态] -->|首次访问| B[偏向锁]
B -->|竞争发生| C[轻量级锁]
C -->|竞争加剧| D[重量级锁]
三、线程池设计与参数配置
核心参数解析
ThreadPoolExecutor(
int corePoolSize, // 常驻线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue // 任务队列
)
队列选择策略
| 队列类型 | 特点 | 适用场景 |
|---|---|---|
SynchronousQueue |
直接传递任务,无缓冲 | 高吞吐量场景 |
ArrayBlockingQueue |
固定容量FIFO队列 | 流量平稳场景 |
LinkedBlockingQueue |
无界队列(默认Integer.MAX_VALUE) | 任务堆积缓冲场景 |
拒绝策略对比
// 四大内置策略
new ThreadPoolExecutor.AbortPolicy() // 直接抛出RejectedExecutionException
new ThreadPoolExecutor.CallerRunsPolicy() // 由提交任务的线程执行
new ThreadPoolExecutor.DiscardPolicy() // 静默丢弃新任务
new ThreadPoolExecutor.DiscardOldestPolicy() // 丢弃队列头任务并重试
四、综合实战案例
订单支付系统线程模型
// 创建带监控的线程池
ExecutorService payExecutor = new ThreadPoolExecutor(
4, 8, 30, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000),
new NamedThreadFactory("Pay-Thread"),
new CountableRejectedPolicy() // 自定义记录拒绝次数的策略
);
// 执行支付任务
payExecutor.execute(() -> {
PaymentService.process(order);
// 包含DB操作、第三方支付接口调用等
});
性能优化关键点
-
线程数公式参考:
$$ N_{threads} = N_{CPU} \times U_{CPU} \times (1 + \frac{W}{C}) $$
$N_{CPU}$: CPU核心数, $U_{CPU}$: 目标CPU利用率, $\frac{W}{C}$: 等待时间与计算时间比 -
上下文切换监控:
通过jstack或Arthas检测线程状态BLOCKED占比
避坑指南:
- 避免在锁内调用外部服务(网络I/O)
- 使用
ThreadLocal后必须显式清除防止内存泄漏- 线程池销毁需调用
shutdownNow()并处理未完成任务
通过深度理解线程安全本质、灵活运用锁机制、合理配置线程池参数,可构建出高稳定性的并发系统。实际开发中需结合JProfiler等工具进行压测验证,持续优化线程模型。
更多推荐



所有评论(0)