一、并发编程基础概念

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 原理

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐