并发

Java并发编程核心知识点整理

一、线程中断机制(interrupt)

为什么interrupt()可以中断线程?

interrupt()方法并不是强制中断线程,而是通过设置中断标志的方式,由线程自己决定如何处理中断。

两种中断场景:

  1. 线程处于阻塞状态(如sleep()wait()join()

    • 调用interrupt()会立即抛出InterruptedException异常,从而中断线程
  2. 线程正在运行状态

    • interrupt()仅设置中断标志位(true)
    • 需要线程主动检查中断状态(如isInterrupted()),自行决定是否终止执行

二、线程池工作原理

任务执行流程:

  1. 检查核心线程数是否已满
    • 未满 → 创建核心线程执行任务
    • 已满 → 进入步骤2
  2. 检查工作队列是否已满
    • 未满 → 将任务放入队列等待
    • 已满 → 进入步骤3
  3. 检查当前线程数是否超过最大线程数
    • 未超过 → 创建非核心线程执行任务
    • 超过 → 执行拒绝策略

线程池核心参数:

参数 说明
核心线程数 常驻的核心线程数量
最大线程数 核心线程数 + 非核心线程数
工作队列 存放等待执行的任务
拒绝策略 任务无法执行时的处理方式

最佳实践:

手动创建线程池,根据业务场景合理配置参数,避免使用Executors默认实现(可能引发OOM)。


三、CountDownLatch详解

作用:

允许一个或多个线程等待其他线程完成操作后再继续执行。

实现原理:

  • 通过计数器实现
  • await():使当前线程等待,直到计数器为0
  • countDown():计数器减1
  • 计数器减至0后不会重置

四、单例模式中的volatile

问题:

为什么使用了synchronized还需要volatile

原因:

防止指令重排序

对象创建三步过程:

  1. 分配内存空间
  2. 初始化对象
  3. 将内存地址赋值给引用

风险:

如果没有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锁升级过程

锁的四种状态(由低到高):

  1. 无锁状态

    • 没有线程获取锁
  2. 偏向锁

    • 只有一个线程获取锁
    • 性能优于正常锁
  3. 轻量级锁

    • 多个线程通过CAS自旋竞争锁
  4. 重量级锁

    • CAS自旋超过10次(或自旋线程数超过CPU核心数一半)
    • 升级为重量级锁,进入操作系统内核态阻塞

总结

理解Java并发编程的核心机制,对于编写高性能、线程安全的程序至关重要。本文总结了线程中断、线程池、锁机制、并发工具等关键知识点,希望对您的学习和工作有所帮助。

posted @ 2026-03-14 12:01  捞马仕途  阅读(2)  评论(0)    收藏  举报