学习记录 03/26 【上】

66 阅读7分钟

一、多线程基础概念

  1. 进程与线程的区别
    1. 进程中可能包含多个线程,线程是CPU调度的基本单位。
  2. 线程创建方式(Thread/Runnable/Callable+线程池)
    1. 根本上都是使用Runnable接口创建,使用Thread类启动。
  3. 线程生命周期与状态转换(NEW/RUNNABLE/BLOCKED/WAITING/TIMED_WAITING/TERMINATED)
  4. 守护线程与用户线程的区别
    1. 守护线程服务于用户线程,当用户线程全都销毁后自动销毁,JVM退出时无需等待守护线程执行完成。用户线程全部执行完毕且销毁后JVM才会退出。
    2. 通过setDaemon(true)设置,需在start()前调用。

二、Java多线程核心类与API

  1. Thread类
    • start()与run()区别
      • start()会启动一个线程执行run()方法的内容。
      • run()只是一个普通方法,不会新建线程执行。
    • sleep() vs yield() vs join()
      • sleep()会让线程休眠。
      • yield()只会让出CPU时间片,不会休眠。
      • join()会让当前线程等待join的线程执行完毕才开始执行。
    • interrupt()中断机制
  2. synchronized关键字
    • 非公平锁
    • 对象锁与类锁
    • 同步方法与同步代码块
      • 当加在静态方法上时会使用类锁。
      • 加在成员方法上时会锁住当前对象。
      • 加在同步代码块上以指定的对象为锁,使用class对象即为类锁,使用普通对象即为对象锁。
    • 锁升级过程(无锁->偏向锁→轻量级锁→重量级锁)
      • 对象创建时为无锁状态。
      • 有一个线程获取锁时,将锁对象的对象头中锁标志置为偏向锁,线程ID为当前线程。
      • 当再来另外一个线程获取锁时,会升级为轻量级锁,CAS自旋获取锁。
      • 当锁竞争比较激烈,即自旋达到一定次数没有获取到锁时会升级为重量级锁,未获取到锁的线程会进入阻塞状态。
  3. wait()/notify()机制
    • 生产者-消费者模型实现
      • 生产队列满了,生产者调用wait()等待消费者消费并通过notify()唤醒生产者。
    • 虚假唤醒问题与解决方案
      • 需用while循环检查条件而非if

三、java.util.concurrent并发工具包

  1. 锁机制

    • ReentrantLock(公平锁/非公平锁)
      • 支持公平锁和非公平锁两种状态,在创建锁时可以通过构造函数的参数进行指定。
    • ReadWriteLock(读写锁)
      • 读写分离,同时可以有多个线程获取读锁。
      • 读锁与写锁互斥,读锁不能升级为写锁,但写锁能够降级为读锁,通过获取读锁,释放写锁完成降级。
    • Condition条件队列
      • ReentrantLock支持同时具有多个Condition条件队列,更加灵活。
  2. 原子类(AtomicXXX)

    • CAS原理与ABA问题
      • CAS通过先比较再交换保证线程安全。
      • ABA问题是CAS获取到的A是修改成B又修改成A的那个A,解决办法是加递增版本号(AtomicStampedReference类提供了解决办法)。
    • LongAdder高性能实现
  3. 线程池

    • ThreadPoolExecutor核心参数(corePoolSize/maxPoolSize/workQueue/rejectPolicy

      • corePoolSize:核心线程数,常驻在线程池,但是也可以通过设置allowCoreThreadTimeOut参数为true让核心线程可以被回收,默认为false
      • maxPoolSize:最大线程数,最多可以创建的线程数量
      • workQueue:任务队列,当核心线程都在执行任务时,将任务暂存在任务队列中。
      • RejectedExecutionHandler:拒绝策略,AbortPolicy(抛异常)、CallerRunsPolicy(调用者执行)、DiscardOldestPolicy(丢弃最旧任务)、DiscardPolicy(静默丢弃)
    • 线程池工作流程与状态转换

      • 任务提交 → 核心线程满则入队 → 队列满则创建新线程 → 超过最大线程则触发拒绝策略
    • Executors工具类(newCachedThreadPool/newFixedThreadPool风险)

      • newCachedThreadPool可能会创建过多的线程

        • 1. 核心参数

          • 核心线程数:0
          • 最大线程数Integer.MAX_VALUE
          • 队列类型SynchronousQueue(无容量缓冲队列,生产者插入需等待消费者消费)
          • 线程存活时间:60秒(空闲线程超时回收)

          2. 适用场景

          • 短期突发任务:如瞬时高并发请求(例如秒杀系统的瞬时流量处理)。
          • 异步IO密集型任务:如HTTP短连接请求、文件下载等,任务执行时间短且线程复用率高。

          3. 风险与缺陷

          • 线程爆炸:任务提交速度远超处理速度时,可能创建大量线程(理论可达

            Integer.MAX_VALUE
            

            ),导致系统资源(CPU/内存)耗尽。

          • 上下文切换开销:线程数过多时,频繁的线程切换会降低CPU利用率,甚至触发系统熔断。

          • 不适用长耗时任务:线程长期占用可能导致队列积压和拒绝策略失效。

      • newFixedThreadPool可能会在队列中存入太多的任务导致内存溢出

        • 核心参数

          • 核心线程数:用户指定
          • 最大线程数:与核心线程数相同
          • 队列类型:LinkedBlockingQueue(无界队列,默认容量Integer.MAX_VALUE

          2. 适用场景

          • 稳定并发任务:如后台定时任务处理、日志批量写入等,线程数固定且任务量可控的场景。
          • CPU密集型任务:避免线程过多导致的上下文切换损耗。

          3. 风险与缺陷

          • 内存溢出(OOM):无界队列可能无限堆积任务,导致内存耗尽(例如电商系统秒杀活动时订单积压)。
          • 响应延迟:队列过长时,新任务需等待较长时间才能被处理。
          • 线程僵死:若线程因异常终止且未重启,可能导致任务堆积但无可用线程处理
  4. 并发集合类

    • ConcurrentHashMap(JDK7分段锁 vs JDK8 CAS+sychronized),03/25已总结
    • CopyOnWriteArrayList适用场景
      • 适用于读多写少的场景,03/25已总结
    • BlockingQueue实现类(Array/Linked/ Priority/Synchronous)

四、线程通信与协作

  1. CountDownLatch(一次性屏障):一次性屏障,主线程等待N个子任务完成
  2. CyclicBarrier(可重用屏障):可重复使用,所有线程到达屏障后继续执行
  3. Semaphore(信号量模型):用于控制并发线程数,如数据库连接池限制

五、高级主题

  1. volatile关键字
    • 内存可见性原理(MESI协议)
      • 通过内存屏障禁止缓存,直接读写主内存
    • 禁止指令重排序(内存屏障)
      • 禁止指令重排序(单例模式双重检查锁配合volatile
  2. ThreadLocal
    • 原理与内存泄漏问题
      • 每个线程独立存储数据,通过ThreadLocalMap实现(Key为弱引用防止内存泄漏)
      • 需手动调用remove()清理Entry
  3. Java内存模型(JMM)
    • 主内存与工作内存
    • happens-before规则
    • 原子性/可见性/有序性保证

六、多线程设计模式

  1. 生产者-消费者模式(BlockingQueue实现)
  2. Thread-Per-Message模式
  3. Worker Thread模式(线程池应用)
  4. Balking模式(任务丢弃策略)
  5. Guarded Suspension模式(条件等待)

七、性能优化与调试

  1. 线程安全的最佳实践
    • 无状态对象
    • 不可变对象
    • 线程封闭(栈封闭/ThreadLocal)
  2. 死锁排查与解决
    • jstack诊断死锁
    • 锁顺序化解决方案
  3. 性能优化策略
    • 减少锁粒度(ConcurrentHashMap分桶)
    • 无锁编程(CAS/原子类)
    • 避免伪共享(@Contended注解)

八、常见面试题专题

  1. synchronized与ReentrantLock的区别
  2. ThreadLocal内存泄漏原因与解决方案
  3. 线程池任务执行流程与拒绝策略
  4. ConcurrentHashMap扩容机制(JDK8)
  5. 如何实现线程安全单例模式(DCL+volatile)
  6. volatile能否保证原子性?
  7. 如何排查高CPU占用线程?

九、扩展知识

  1. Java8+新特性

    • CompletableFuture异步编程
    • StampedLock(乐观读锁)
  2. 分布式并发问题

    • 分布式锁(Redis/ Zookeeper)
    • 数据库锁机制(乐观锁/悲观锁)
  3. 高并发设计

    • 限流算法(令牌桶/漏桶)
    • 熔断降级策略
  4. LeetCode 34.在排序数组中查找元素的第一个和最后一个位置 2. 考察二分查找,二分查找需要注意边界