当前位置: 首页 > backend >正文

java面试中经常会问到的多线程问题有哪些(基础版)

文章目录

      • 一、线程基础与生命周期
      • 二、并发安全与同步机制
      • 三、线程池核心问题
      • 四、锁机制与并发工具
      • 五、并发容器
      • 六、实战场景问题
      • 总结

Java 多线程是面试中的高频考点,涉及线程基础、并发安全、线程池、锁机制等核心内容。以下是面试中经常出现的多线程问题及核心考察点:

一、线程基础与生命周期

  1. 线程和进程的区别?
  • 进程是资源分配的最小单位(拥有独立内存、CPU 等资源),线程是 CPU 调度的最小单位(共享进程资源)。
  • 进程间切换开销大,线程间切换开销小;一个进程可包含多个线程。
  1. Java 中创建线程的方式有哪些?
  • 继承 Thread 类(重写 run() 方法)。
  • 实现 Runnable 接口(重写 run() 方法,通过 Thread 包装)。
  • 实现 Callable 接口(重写 call() 方法,可返回结果,通过 FutureTask 配合 Thread)。
  • 考察点:RunnableCallable 的区别(返回值、异常处理)、为什么推荐实现接口而非继承 Thread(Java 单继承限制)。
  1. 线程的生命周期有哪些状态?状态之间如何转换?
  • 6 种状态(JDK 定义):NEW(新建)→ RUNNABLE(就绪/运行)→ BLOCKED(阻塞,等待锁)→ WAITING(无限等待)→ TIMED_WAITING(超时等待)→ TERMINATED(终止)。
  • 关键转换:wait() 使线程从 RUNNABLEWAITING(需 notify() 唤醒);sleep(1000) 使线程 → TIMED_WAITING(时间到后自动唤醒);争夺锁失败 → BLOCKED(获取锁后回到 RUNNABLE)。

二、并发安全与同步机制

  1. 什么是线程安全?如何保证线程安全?
  • 线程安全:多线程并发访问时,程序行为符合预期(结果正确、无数据混乱)。
  • 保证方式:synchronized 关键字、volatile 关键字、Lock 锁(ReentrantLock)、原子类(AtomicInteger)、线程封闭(ThreadLocal)等。
  1. synchronized 的实现原理?与 Lock 的区别?
  • 原理:基于 JVM 内置锁(监视器锁 monitor),通过 ACC_SYNCHRONIZED 标志(方法)或 monitorenter/monitorexit 指令(代码块)实现。
  • 区别:
 | 维度         | `synchronized`                | `Lock`(如 `ReentrantLock`)         |  |--------------|--------------------------------|--------------------------------------|  | 锁释放       | 自动释放(异常或方法结束)     | 需手动 `unlock()`(通常在 `finally` 中) |  | 灵活性       | 不可中断、不可超时             | 支持中断、超时、公平锁               |  | 条件变量     | 仅通过 `wait()`/`notify()`     | 支持多条件变量(`Condition`)        |  
  1. volatile 的作用?能保证原子性吗?
  • 作用:保证变量的可见性(一个线程修改后,其他线程立即可见)和禁止指令重排序(如单例模式的双重检查锁需用 volatile 修饰实例)。
  • 不能保证原子性:例如 i++(读-改-写三步操作),多线程下可能出现数据不一致,需配合 synchronized 或原子类。
  1. ThreadLocal 的原理?可能导致什么问题?
  • 原理:每个线程有独立的 ThreadLocalMap,存储线程私有变量(键为 ThreadLocal 实例,值为变量),实现线程隔离。
  • 问题:线程池复用线程时,若 ThreadLocal 未及时 remove(),会导致变量被复用(内存泄漏);底层 Entry 是弱引用(Key 弱引用,Value 强引用),可能导致 Value 无法回收。

三、线程池核心问题

  1. 线程池的核心参数有哪些?工作原理是什么?
  • 核心参数:corePoolSize(核心线程数)、maximumPoolSize(最大线程数)、keepAliveTime(非核心线程空闲存活时间)、workQueue(任务队列)、threadFactory(线程工厂)、handler(拒绝策略)。
  • 工作原理:
    1. 任务提交时,若核心线程未满,创建核心线程执行任务;
    2. 核心线程满,任务放入队列;
    3. 队列满,创建非核心线程执行任务;
    4. 总线程数达最大线程数,触发拒绝策略(如 AbortPolicy 抛出异常)。
  1. 线程池的拒绝策略有哪些?
  • AbortPolicy(默认):直接抛出 RejectedExecutionException
  • CallerRunsPolicy:让提交任务的线程自己执行(缓解压力)。
  • DiscardPolicy:默默丢弃任务。
  • DiscardOldestPolicy:丢弃队列中最旧的任务,再尝试提交当前任务。
  1. 如何合理配置线程池参数?
  • CPU 密集型任务(如计算):核心线程数 = CPU 核心数 + 1(减少线程切换开销)。
  • IO 密集型任务(如网络请求、数据库操作):核心线程数 = CPU 核心数 × 2(利用 IO 等待时间并行处理)。
  • 关键:结合任务类型(CPU/IO 密集)、队列容量(避免过大导致 OOM)、拒绝策略(根据业务容忍度选择)。

四、锁机制与并发工具

  1. 什么是死锁?如何避免死锁?
  • 死锁:两个或多个线程互相持有对方需要的锁,无限等待(如线程 A 持有锁 1 等待锁 2,线程 B 持有锁 2 等待锁 1)。
  • 避免:固定锁的获取顺序、使用 tryLock(timeout) 超时获取锁、使用 LocklockInterruptibly() 响应中断、定期检测死锁(ThreadMXBean)。
  1. 公平锁与非公平锁的区别?ReentrantLock 默认是哪种?
  • 公平锁:线程获取锁的顺序按请求顺序(FIFO),避免饥饿,但性能较低。
  • 非公平锁:允许“插队”(刚释放锁的线程可再次获取锁),性能高,但可能导致线程饥饿。
  • ReentrantLock 默认是非公平锁(通过构造函数 new ReentrantLock(true) 开启公平锁)。
  1. Java 中的原子类(如 AtomicInteger)如何保证原子性?
  • 基于 CAS(Compare And Swap,比较并交换)操作:通过 Unsafe 类的 native 方法,直接操作内存,实现无锁的原子更新(如 compareAndSet(expected, update))。
  • 问题:ABA 问题(可通过 AtomicStampedReference 加版本号解决)、循环时间长导致 CPU 开销大。
  1. CountDownLatchCyclicBarrierSemaphore 的区别?
  • CountDownLatch:一个线程等待其他 N 个线程完成(倒计时,不可重置)。
  • CyclicBarrier:N 个线程互相等待,全部到达后一起执行(可重置,支持回调)。
  • Semaphore:控制同时访问资源的线程数(如限制并发连接数)。

五、并发容器

  1. HashMap 为什么线程不安全?ConcurrentHashMap 如何实现线程安全?
  • HashMap 线程不安全:多线程扩容时可能出现链表环(JDK 7),或 put 时覆盖数据。
  • ConcurrentHashMap(JDK 1.8):采用“数组 + 链表/红黑树”,通过 CAS + synchronized 同步链表头节点实现线程安全(粒度比 JDK 7 的分段锁更细,性能更高)。
  1. ArrayListVector 的区别?CopyOnWriteArrayList 适合什么场景?
  • Vector 线程安全(方法加 synchronized),但性能低;ArrayList 线程不安全。
  • CopyOnWriteArrayList:写操作时复制底层数组(add/set 会创建新数组),读操作无锁,适合读多写少场景(如配置缓存),但存在数据一致性延迟和内存开销。

六、实战场景问题

  1. 如何实现一个生产者-消费者模型?
  • 基于 synchronized + wait()/notify():用队列存储任务,生产者 put 时通知消费者,消费者 take 时等待(队列空)。
  • 基于 Lock + Condition:更灵活(生产者和消费者可使用不同 Condition 唤醒)。
  • 基于阻塞队列(ArrayBlockingQueue):简化实现(队列自带阻塞功能)。
  1. 单例模式的线程安全实现?双重检查锁为什么需要 volatile
  • 线程安全单例:饿汉式(类加载时初始化)、懒汉式(synchronized 方法)、双重检查锁(DCL)、静态内部类。
  • DCL 中的 volatile:防止指令重排序(实例化对象的步骤:分配内存 → 初始化 → 赋值,重排序可能导致其他线程获取到未初始化的实例)。
  1. 线程池中的线程抛出异常会怎样?如何处理?
  • 若任务通过 execute() 提交:异常会直接抛出,线程池会销毁该线程并新建线程。
  • 若通过 submit() 提交:异常会被封装到 Future 中,需调用 get() 才能获取异常。
  • 处理:重写线程池的 afterExecute() 方法,或在任务内部 try-catch 捕获异常。

总结

多线程面试重点考察基础概念理解(线程状态、锁机制)、并发安全保障synchronizedvolatile、原子类)、线程池原理与配置实战问题解决能力(死锁、生产者-消费者)。回答时需结合底层原理(如 CAS、监视器锁)和实际场景(如线程池参数配置依据),体现对并发编程的深度理解。

http://www.xdnf.cn/news/19665.html

相关文章:

  • think
  • ubuntu系统设置中文失败问题
  • grpc-swift-2 学习笔记
  • 均匀分布直线阵的常规波束形成方位谱和波束图
  • (Arxiv-2025)ConceptMaster:基于扩散 Transformer 模型的多概念视频定制,无需测试时微调
  • 【2025终极对决】Python三大后端框架Django vs FastAPI vs Robyn,你的选择将决定项目生死?
  • [光学原理与应用-366]:ZEMAX - 用成像原理说明人眼为什么能看清物体?
  • 两款超实用办公插件推荐:Excel聚光灯与Word公文排版
  • MySQL 多表查询方法
  • Spring Boot 全局字段处理最佳实践
  • mysql初学者练习题(从基础到进阶,相关数据sql脚本在最后)
  • 59.螺旋矩阵II
  • 框架-SpringMVC-1
  • WPF中的静态资源和动态资源
  • 支付系统设计模式应用:从单例到观察者模式实践
  • 网络编程 05:UDP 连接,UDP 与 TCP 的区别,实现 UDP 消息发送和接收,通过 URL 下载资源
  • EPLAN 分散式端子:提升原理图设计效率的实用功能
  • 使用 C 模仿 C++ 模板的拙劣方法
  • Replit在线编程工具:支持多语言环境免配置与实时协作,助力编程学习调试与社区项目复用
  • 企业微信员工聊天记录能看吗?合规管理三要素一次性说清
  • cuDNN深度解析:实战演练
  • Electron 菜单与托盘:构建用户友好的界面元素
  • 9月2日
  • 深入分析 json2(新)与标准的 jsonrpc的区别
  • zephyr设备树的硬件描述转换为c语言
  • Hash 算法 SHA-1、SHA-256、SHA-384、SHA-512 对比
  • SpringBoot3 + Netty + Vue3 实现消息推送(最新)
  • 食品分类案例
  • 码住!辉芒微MCU型号规则详细解析
  • Kafka 架构详解