DukeDuke
主页
项目文档
技术文档
  • 单机版
  • 微服务
  • 代办项目
  • 优鲜项目
项目管理
关于我们
主页
项目文档
技术文档
  • 单机版
  • 微服务
  • 代办项目
  • 优鲜项目
项目管理
关于我们
  • 技术文档

    • 网络原理

      • 交换机
      • 路由器
      • TCP/IP协议
      • HTTP 与 HTTPS
    • 软件架构

      • 什么是软件架构
      • 分层架构
      • 微服务架构
      • 事件驱动架构
      • 领域驱动设计(DDD)
      • 架构图
      • 高并发系统
    • Vue3

      • Vue3简介
      • Vue3响应式系统
      • Vue3组合式API
      • Vue3生命周期
      • Vue3模板语法
      • Vue3组件系统
      • Vue3 路由系统
      • Vue3 状态管理
      • Vue3 性能优化
      • Vue3 TypeScript 支持
      • Vue3 项目实战
      • VUE 面试题大全
      • Node.js 安装
    • JAVA

      • JVM

        • 认识JVM
        • JVM类加载器
        • 运行时数据区
        • 执行引擎
        • 本地方法接口
        • 本地方法库
        • JVM垃圾回收
        • JVM性能监控
        • JVM调优
      • 设计模式
        • 单例模式
        • 工厂模式
        • 策略模式
        • 适配器模式
        • 建造者模式
        • 原型模式
        • 装饰器模式
        • 代理模式
        • 外观模式
        • 享元模式
        • 组合模式
        • 桥接模式
      • Java多线程

        • Java 线程基础详解
        • Java 线程池详解
        • Java ThreadLocal 详解
        • Java volatile 详解
        • Java 线程间通信详解
        • Java 线程安全详解
        • Java 线程调度详解
        • Java 线程优先级详解

        • Java 线程中断详解
        • Java 线程死锁详解
      • Java反射
      • Java 面试题

        • Java 基础概念面试题
        • Java 面向对象编程面试题
        • Java 集合框架面试题
        • Java 多线程与并发面试题
        • JVM 与内存管理面试题
        • Java I/O 与 NIO 面试题
        • Java 异常处理面试题
        • Java 反射与注解面试题
        • Java Spring 框架面试题
        • Java 数据库与 JDBC 面试题
        • Java 性能优化面试题
        • Java 实际项目经验面试题
        • Java 高级特性面试题
        • Java 面试准备建议
    • Python

      • Python简介
      • Python安装
      • Python hello world
      • Python基础语法
      • Python数据类型
      • Python数字
      • Python字符串
      • Python列表
      • Python元组
      • Python字典
      • Python日期时间
      • Python文件操作
      • Python异常处理
      • Python函数
      • Python类
      • Python模块
      • Python包
      • Python多线程
      • Python面向对象
      • Python爬虫
      • Django web框架
      • Python 面试题

        • Python 面试题导航
        • Python 基础概念
        • Python 面向对象编程
        • Python 数据结构
        • Python 高级特性
        • Python 框架
        • Python 性能优化
        • Python 项目经验
    • Spring

      • Spring
      • Springboot
      • Spring Security 安全框架
      • SpringBoot 中的事件详解
      • SpringBoot 中的定时任务详解
      • SpringBoot 自动装配原理与源码解释
    • Mybatis

      • Mybatis
      • Mybatis-Plus
    • 数据库

      • Redis

        • Redis简介
        • Redis(单机)安装
        • Redis配置
        • Redis数据结构
        • RDB、AOF 和混合持久化机制
        • Redis内存管理
        • Redis缓存一致性
        • Redis缓存穿透
        • Redis缓存击穿
        • Redis缓存雪崩
        • Redis Lua脚本
        • Redis主从复制
        • Redis哨兵模式
        • Redis集群
        • Redis数据分片
        • Redis CPU使用率过高
        • Redis面试题
      • MySQL

        • MySQL简介
        • MySQL安装
        • MySQL配置
        • MYSQL日常维护
        • MYSQL优化-慢查询
        • MYSQL优化-索引
        • MYSQL数据库设计规范
    • 消息队列

      • RocketMQ
      • Kafka
      • RabbitMQ
      • 消息队列面试题
    • 微服务

      • SpringCloud 微服务
      • Eureka 注册中心
      • Nacos 注册中心
      • Gateway 网关
      • Feign 服务调用
      • Sentinel 限流 与 熔断
      • Seata 分布式事务
      • CAP 理论
      • Redis 分布式锁
      • 高并发系统设计
    • ELK日志分析系统

      • Elasticsearch 搜索引擎
      • Logstash 数据处理
      • Kibana 可视化
      • ELK 实战
    • 开放API

      • 开放API设计
      • 开放API示例项目
    • 人工智能

      • 人工智能简介
      • 机器学习

      • 深度学习

      • 自然语言处理

      • 计算机视觉

        • CUDA与cuDNN详细安装
        • Conda 安装
        • Pytorch 深度学习框架
        • yolo 目标检测
        • TensorRT 深度学习推理优化引擎
        • TensorFlow 机器学习
        • CVAT 图像标注
        • Windows 下安装 CUDA、cuDNN、TensorRT、TensorRT-YOLO 环境
        • Windows10+CUDA+cuDNN+TensorRT+TensorRT-YOLO 部署高性能YOLO11推理
    • 大数据

      • 大数据简介
      • Hadoop 数据存储
      • Flume 数据采集
      • Sqoop 数据导入导出
      • Hive 数据仓库
      • Spark 数据处理
      • Flink 数据处理
      • Kafka 数据采集
      • HBase 数据存储
      • Elasticsearch 搜索引擎
    • 图像处理

      • 图像处理简介
      • 医学图像web呈现
      • 医学图像处理
      • 切片细胞分离问题
    • 服务器&运维

      • Linux 系统

        • Linux 系统管理
        • Linux 网络管理
        • Linux 文件管理
        • Linux 命令大全
      • Nginx Web 服务器

        • Nginx 安装 与 配置
        • Nginx 负载均衡
        • Nginx SSL证书配置
        • Nginx Keepalived 高可用
      • Docker 容器

        • Docker 简介
        • Docker 安装与配置
        • Docker 命令
        • Docker 部署 Nginx
        • Docker 部署 MySQL
        • Docker 部署 Redis
      • 服务器

        • 塔式服务器
        • 机架式服务器
        • 刀片服务器
      • Git 版本控制
      • Jenkins 持续集成
      • Jmeter 性能测试
      • Let's Encrypt 免费SSL证书
    • 简历

      • 项目经理简历
      • 开发工程师简历

Java 多线程与并发面试题

目录

  • 线程基础
  • 线程创建与生命周期
  • 线程同步
  • 锁机制
  • 线程池
  • 并发工具类
  • 原子操作
  • 内存模型
  • 并发编程最佳实践

线程基础

1. 什么是线程?如何创建线程?

答案要点:

  • 线程的概念
  • 继承 Thread 类
  • 实现 Runnable 接口
  • 实现 Callable 接口
  • 线程池的使用

示例答案: "线程是程序执行的最小单位,是 CPU 调度的基本单位。Java 中创建线程有三种方式:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口。继承 Thread 类是最简单的方式,但 Java 不支持多重继承,限制了类的扩展性。实现 Runnable 接口更灵活,可以继承其他类,适合只需要重写 run 方法的场景。实现 Callable 接口可以返回值和抛出异常,配合 Future 使用。在实际项目中,我推荐使用线程池而不是直接创建线程,线程池可以复用线程,控制并发数量,提供更好的性能和资源管理。ExecutorService 提供了丰富的线程池实现,如 FixedThreadPool、CachedThreadPool 等。"

深入解析:

  • 线程概念:程序执行的最小单位,CPU 调度基本单位
  • 创建方式:继承 Thread、实现 Runnable、实现 Callable
  • 线程池:复用线程,控制并发,提高性能
  • 最佳实践:优先使用线程池

2. 线程的生命周期是怎样的?

答案要点:

  • 线程状态
  • 状态转换
  • 状态监控
  • 实际应用

示例答案: "Java 线程的生命周期包括六个状态:NEW(新建)、RUNNABLE(可运行)、BLOCKED(阻塞)、WAITING(等待)、TIMED_WAITING(超时等待)、TERMINATED(终止)。NEW 状态是线程创建但未启动;RUNNABLE 状态是线程正在运行或等待 CPU 调度;BLOCKED 状态是线程等待获取锁;WAITING 状态是线程无限期等待;TIMED_WAITING 状态是线程有限期等待;TERMINATED 状态是线程执行完毕。在实际项目中,我会监控线程状态,使用 Thread.getState() 获取线程状态,使用 jstack 工具分析线程转储,诊断线程问题。"

深入解析:

  • NEW:线程创建,未调用 start()
  • RUNNABLE:可运行状态,包括运行和就绪
  • BLOCKED:阻塞状态,等待获取锁
  • WAITING:等待状态,无限期等待
  • TIMED_WAITING:超时等待状态
  • TERMINATED:终止状态,线程执行完毕

3. 进程和线程的区别是什么?

答案要点:

  • 资源占用
  • 通信方式
  • 创建开销
  • 安全性

示例答案: "进程和线程在多个方面有重要区别。资源占用方面,进程拥有独立的内存空间,线程共享进程的内存空间。通信方式方面,进程间通信需要 IPC 机制,线程间通信可以直接访问共享变量。创建开销方面,创建进程开销大,创建线程开销小。安全性方面,进程间相互独立,一个进程崩溃不影响其他进程,线程间相互影响,一个线程崩溃可能导致整个进程崩溃。在实际项目中,我会根据需求选择:需要高隔离性使用进程,需要高性能使用线程。"

深入解析:

特性进程线程
内存空间独立共享
通信方式IPC共享变量
创建开销大小
安全性高低
切换开销大小

线程创建与生命周期

4. 为什么推荐实现 Runnable 而不是继承 Thread?

答案要点:

  • 单继承限制
  • 代码复用
  • 设计原则
  • 实际应用

示例答案: "推荐实现 Runnable 而不是继承 Thread 有多个原因。首先,Java 只支持单继承,继承 Thread 会占用继承机会,限制类的扩展性。其次,实现 Runnable 可以继承其他类,提高代码复用性。第三,实现 Runnable 符合面向接口编程的原则,降低耦合度。第四,实现 Runnable 可以更好地实现资源共享,多个线程可以共享同一个 Runnable 对象。在实际项目中,我会优先实现 Runnable 接口,只有在需要重写 Thread 的其他方法时才继承 Thread 类。"

深入解析:

  • 单继承限制:Java 单继承,继承 Thread 占用继承机会
  • 代码复用:实现 Runnable 可以继承其他类
  • 设计原则:面向接口编程,降低耦合
  • 资源共享:多个线程共享 Runnable 对象

5. 如何正确启动和停止线程?

答案要点:

  • 启动线程
  • 停止线程
  • 线程中断
  • 最佳实践

示例答案: "正确启动线程使用 start() 方法,它会创建新的线程并调用 run() 方法。不能直接调用 run() 方法,那只是在当前线程中执行。停止线程应该使用中断机制,调用 interrupt() 方法设置中断标志,线程检查中断状态并响应中断。不能使用已废弃的 stop() 方法,它可能导致数据不一致。在实际项目中,我会使用中断机制停止线程,在 run() 方法中检查 Thread.interrupted() 或 isInterrupted(),及时响应中断请求,确保线程能够安全退出。"

深入解析:

  • 启动线程:使用 start() 方法,创建新线程
  • 停止线程:使用中断机制,检查中断状态
  • 中断处理:Thread.interrupted()、isInterrupted()
  • 安全退出:响应中断,清理资源

6. 什么是守护线程?如何使用守护线程?

答案要点:

  • 守护线程的概念
  • 与用户线程的区别
  • 使用场景
  • 注意事项

示例答案: "守护线程是为其他线程提供服务的线程,当所有用户线程结束时,守护线程会自动结束。守护线程与用户线程的区别在于:用户线程阻止 JVM 退出,守护线程不会阻止 JVM 退出。守护线程通常用于后台任务,如垃圾回收、日志记录、监控等。设置守护线程使用 setDaemon(true) 方法,必须在 start() 之前调用。在实际项目中,我会将后台服务线程设置为守护线程,如定时任务、监控线程等,确保主程序结束时这些线程也能正常退出。"

深入解析:

  • 守护线程:为其他线程提供服务,不阻止 JVM 退出
  • 用户线程:主要业务线程,阻止 JVM 退出
  • 设置方法:setDaemon(true),必须在 start() 前调用
  • 使用场景:后台服务、监控、定时任务

线程同步

7. synchronized 关键字的作用是什么?

答案要点:

  • 线程同步
  • 对象锁和类锁
  • 可重入性
  • 性能考虑

示例答案: "synchronized 是 Java 中实现线程同步的关键字,可以修饰方法或代码块。修饰实例方法时,锁的是当前对象实例;修饰静态方法时,锁的是当前类对象;修饰代码块时,可以指定锁对象。synchronized 具有可重入性,同一个线程可以多次获取同一个锁。synchronized 是悲观锁,在竞争激烈的情况下性能较差,因为线程会进入阻塞状态。在实际项目中,我会根据同步需求选择合适的锁机制,对于简单的同步需求使用 synchronized,对于复杂的并发控制使用 ReentrantLock、ReadWriteLock 等。还会考虑使用 volatile 关键字保证可见性,使用 AtomicInteger 等原子类避免锁竞争。"

深入解析:

  • 同步机制:保证同一时间只有一个线程访问
  • 锁类型:对象锁、类锁
  • 可重入性:同一线程可多次获取锁
  • 性能影响:悲观锁,竞争激烈时性能差

8. volatile 关键字的作用是什么?

答案要点:

  • 可见性保证
  • 禁止指令重排
  • 不保证原子性
  • 使用场景

示例答案: "volatile 关键字主要用于保证变量的可见性和禁止指令重排序。当一个变量被 volatile 修饰时,一个线程对该变量的修改会立即对其他线程可见,避免了缓存一致性问题。volatile 还通过内存屏障禁止编译器和处理器对指令进行重排序,保证程序执行的有序性。但是,volatile 不保证原子性,对于复合操作(如 i++)仍然需要同步机制。volatile 适合作为状态标志,如线程的停止标志,或者作为双重检查锁定模式中的变量修饰符。在实际项目中,我会使用 volatile 保证简单变量的可见性,对于复杂的同步需求,会结合 synchronized 或 Lock 使用。"

深入解析:

  • 可见性:修改立即对其他线程可见
  • 有序性:禁止指令重排序
  • 原子性:不保证复合操作的原子性
  • 使用场景:状态标志、双重检查锁定

9. 什么是死锁?如何避免死锁?

答案要点:

  • 死锁的定义
  • 死锁产生的条件
  • 死锁检测
  • 避免策略

示例答案: "死锁是指两个或多个线程相互等待对方释放资源,导致所有线程都无法继续执行的状态。死锁产生的四个必要条件是:互斥条件、请求和保持条件、不剥夺条件、循环等待条件。检测死锁可以使用 jstack 工具分析线程转储,查看线程的锁等待关系。避免死锁的策略包括:按顺序获取锁,避免循环等待;使用超时机制,避免无限等待;使用 tryLock() 方法,获取锁失败时释放已持有的锁;减少锁的粒度,使用更细粒度的锁。在实际项目中,我会设计锁的获取顺序,使用超时机制,定期检查死锁情况。"

深入解析:

  • 死锁条件:互斥、请求保持、不剥夺、循环等待
  • 检测方法:jstack 分析线程转储
  • 避免策略:顺序获取锁、超时机制、tryLock()
  • 预防措施:减少锁粒度、避免嵌套锁

锁机制

10. ReentrantLock 和 synchronized 的区别是什么?

答案要点:

  • 实现机制
  • 功能特性
  • 性能差异
  • 使用场景

示例答案: "ReentrantLock 和 synchronized 在多个方面有重要区别。实现机制方面,synchronized 是 JVM 内置的锁机制,ReentrantLock 是 JDK 实现的锁。功能特性方面,ReentrantLock 提供更多功能,如可中断锁、超时锁、公平锁等。性能方面,在低竞争情况下 synchronized 性能更好,在高竞争情况下 ReentrantLock 性能更好。使用场景方面,synchronized 适合简单的同步需求,ReentrantLock 适合复杂的并发控制。在实际项目中,我会根据具体需求选择:简单同步使用 synchronized,需要高级功能使用 ReentrantLock。"

深入解析:

特性synchronizedReentrantLock
实现JVM 内置JDK 实现
功能基础同步高级功能
性能低竞争好高竞争好
使用简单复杂

11. 什么是读写锁?如何使用读写锁?

答案要点:

  • 读写锁的概念
  • ReadWriteLock 接口
  • ReentrantReadWriteLock 实现
  • 使用场景

示例答案: "读写锁是一种特殊的锁机制,允许多个线程同时读取,但只允许一个线程写入。ReadWriteLock 接口定义了读写锁的规范,ReentrantReadWriteLock 是其实现。读写锁提供了 readLock() 和 writeLock() 方法获取读锁和写锁。读锁之间不互斥,读锁和写锁互斥,写锁之间互斥。读写锁适用于读多写少的场景,如缓存系统、配置管理等。在实际项目中,我会在读多写少的场景下使用读写锁,提高并发性能,注意避免写锁饥饿问题。"

深入解析:

  • 读锁:多个线程可同时获取,与写锁互斥
  • 写锁:独占锁,与读锁和其他写锁互斥
  • 适用场景:读多写少,如缓存、配置
  • 注意事项:避免写锁饥饿

12. 什么是条件变量?如何使用条件变量?

答案要点:

  • 条件变量的概念
  • Condition 接口
  • await() 和 signal() 方法
  • 使用场景

示例答案: "条件变量是一种线程同步机制,允许线程等待特定条件满足。Condition 接口提供了 await()、signal()、signalAll() 等方法。await() 方法使当前线程等待,signal() 方法唤醒一个等待线程,signalAll() 方法唤醒所有等待线程。条件变量通常与锁配合使用,实现复杂的同步逻辑。在实际项目中,我会使用条件变量实现生产者-消费者模式、等待-通知模式等,注意使用 while 循环检查条件,避免虚假唤醒。"

深入解析:

  • await():等待条件满足
  • signal():唤醒一个等待线程
  • signalAll():唤醒所有等待线程
  • 使用模式:while 循环检查条件

线程池

13. 什么是线程池?如何合理配置线程池?

答案要点:

  • 线程池的优势
  • 核心参数
  • 拒绝策略
  • 监控和调优

示例答案: "线程池是一种线程复用机制,通过预先创建一定数量的线程,避免频繁创建和销毁线程的开销。线程池的核心参数包括核心线程数、最大线程数、线程存活时间、工作队列和拒绝策略。核心线程数是线程池中保持活跃的线程数量,最大线程数是允许创建的最大线程数量。工作队列用于存储等待执行的任务,常用的有 ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue 等。拒绝策略决定当线程池和队列都满时如何处理新任务。在实际项目中,我会根据任务类型和系统资源合理配置线程池,CPU 密集型任务使用核心数 + 1 的线程数,IO 密集型任务使用核心数 * 2 的线程数。还会监控线程池的运行状态,及时调整参数。"

深入解析:

  • 核心线程数:保持活跃的线程数量
  • 最大线程数:允许创建的最大线程数量
  • 工作队列:存储等待执行的任务
  • 拒绝策略:队列满时的处理策略

14. 线程池的拒绝策略有哪些?

答案要点:

  • AbortPolicy
  • CallerRunsPolicy
  • DiscardPolicy
  • DiscardOldestPolicy

示例答案: "线程池的拒绝策略有四种:AbortPolicy 是默认策略,直接抛出 RejectedExecutionException 异常;CallerRunsPolicy 由调用线程执行任务,降低提交速度;DiscardPolicy 直接丢弃任务,不抛出异常;DiscardOldestPolicy 丢弃队列中最老的任务,然后重试提交。在实际项目中,我会根据业务需求选择合适的拒绝策略:对于重要任务使用 AbortPolicy,确保任务不丢失;对于可以延迟的任务使用 CallerRunsPolicy,降低提交速度;对于可以丢弃的任务使用 DiscardPolicy。"

深入解析:

  • AbortPolicy:抛出异常,默认策略
  • CallerRunsPolicy:调用线程执行
  • DiscardPolicy:直接丢弃
  • DiscardOldestPolicy:丢弃最老任务

15. 如何监控线程池的运行状态?

答案要点:

  • 线程池状态
  • 监控指标
  • 监控工具
  • 调优策略

示例答案: "监控线程池的运行状态需要关注多个指标。线程池状态包括 RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED。监控指标包括:活跃线程数、队列大小、完成任务数、拒绝任务数等。可以使用 ThreadPoolExecutor 提供的方法获取这些指标,如 getActiveCount()、getQueue().size() 等。在实际项目中,我会定期监控这些指标,设置告警阈值,当指标异常时及时调整线程池参数。还会使用 JMX 或自定义监控系统进行长期监控。"

深入解析:

  • 状态监控:RUNNING、SHUTDOWN、STOP 等
  • 指标监控:活跃线程、队列大小、完成任务
  • 监控工具:JMX、自定义监控
  • 调优策略:根据指标调整参数

并发工具类

16. CountDownLatch 的作用和使用场景是什么?

答案要点:

  • CountDownLatch 的概念
  • 主要方法
  • 使用场景
  • 注意事项

示例答案: "CountDownLatch 是一个同步辅助类,允许一个或多个线程等待其他线程完成操作。CountDownLatch 使用计数器实现,初始化为一个正数,每次调用 countDown() 方法计数器减 1,当计数器为 0 时,等待的线程被唤醒。主要方法包括 await() 等待计数器为 0,countDown() 计数器减 1。CountDownLatch 适用于一个线程等待多个线程完成的场景,如主线程等待多个子线程完成初始化。在实际项目中,我会使用 CountDownLatch 实现线程间的协调,注意计数器只能使用一次,不能重置。"

深入解析:

  • 计数器机制:初始正数,countDown() 减 1
  • 等待机制:await() 等待计数器为 0
  • 使用场景:一个线程等待多个线程完成
  • 注意事项:一次性使用,不能重置

17. CyclicBarrier 和 CountDownLatch 的区别是什么?

答案要点:

  • 使用场景
  • 重置能力
  • 线程数量
  • 异常处理

示例答案: "CyclicBarrier 和 CountDownLatch 在多个方面有重要区别。使用场景方面,CountDownLatch 是一个线程等待多个线程完成,CyclicBarrier 是多个线程相互等待。重置能力方面,CountDownLatch 不能重置,CyclicBarrier 可以重复使用。线程数量方面,CountDownLatch 的计数器可以大于等待线程数,CyclicBarrier 的屏障数必须等于等待线程数。异常处理方面,CyclicBarrier 提供了更好的异常处理机制。在实际项目中,我会根据具体需求选择:需要等待多个线程完成使用 CountDownLatch,需要多个线程同步使用 CyclicBarrier。"

深入解析:

特性CountDownLatchCyclicBarrier
使用场景一个等多个多个相互等
重置能力不能重置可以重复使用
线程数量计数器可大于线程数屏障数等于线程数
异常处理基础更好

18. Semaphore 的作用和使用场景是什么?

答案要点:

  • Semaphore 的概念
  • 信号量机制
  • 主要方法
  • 使用场景

示例答案: "Semaphore 是一个计数信号量,用于控制同时访问特定资源的线程数量。Semaphore 维护一个许可证计数器,acquire() 方法获取许可证,release() 方法释放许可证。当许可证数量为 0 时,acquire() 方法会阻塞。Semaphore 适用于限制并发访问的场景,如数据库连接池、文件访问等。在实际项目中,我会使用 Semaphore 控制资源访问,如限制同时下载的文件数量、限制同时访问的数据库连接数等。"

深入解析:

  • 信号量机制:维护许可证计数器
  • 获取许可证:acquire() 方法
  • 释放许可证:release() 方法
  • 使用场景:限制并发访问

原子操作

19. 什么是原子操作?Java 中有哪些原子类?

答案要点:

  • 原子操作的概念
  • 原子类分类
  • 主要方法
  • 使用场景

示例答案: "原子操作是不可分割的操作,要么全部执行,要么全部不执行。Java 提供了丰富的原子类,包括 AtomicInteger、AtomicLong、AtomicBoolean 等基本类型原子类,AtomicReference 等引用类型原子类,AtomicIntegerArray 等数组类型原子类。原子类使用 CAS(Compare and Swap)操作实现无锁并发,性能比 synchronized 更好。在实际项目中,我会使用原子类实现计数器、状态标志等,避免使用 synchronized 的性能开销。"

深入解析:

  • 原子操作:不可分割的操作
  • CAS 机制:Compare and Swap 无锁并发
  • 原子类:AtomicInteger、AtomicLong、AtomicReference 等
  • 性能优势:比 synchronized 性能更好

20. CAS 操作的原理是什么?

答案要点:

  • CAS 的概念
  • 实现原理
  • ABA 问题
  • 解决方案

示例答案: "CAS(Compare and Swap)是一种无锁并发算法,通过比较内存值和期望值,如果相等则更新为新值。CAS 操作包括三个参数:内存地址、期望值、新值。如果内存中的值等于期望值,则更新为新值,否则操作失败。CAS 操作是原子的,由 CPU 指令保证。CAS 存在 ABA 问题,即值从 A 变为 B 再变为 A,CAS 操作会认为值没有变化。解决 ABA 问题可以使用版本号或 AtomicStampedReference。在实际项目中,我会使用 CAS 操作实现无锁并发,注意处理 ABA 问题。"

深入解析:

  • CAS 操作:比较并交换,原子操作
  • 实现原理:CPU 指令保证原子性
  • ABA 问题:值变化但最终相同
  • 解决方案:版本号、AtomicStampedReference

内存模型

21. Java 内存模型(JMM)是什么?

答案要点:

  • JMM 的概念
  • 主内存和工作内存
  • 内存可见性
  • happens-before 规则

示例答案: "Java 内存模型(JMM)定义了多线程程序中变量的访问规则,确保多线程程序的正确性。JMM 将内存分为主内存和工作内存,每个线程都有自己的工作内存,存储变量的副本。线程对变量的操作在工作内存中进行,然后同步到主内存。JMM 通过 happens-before 规则保证内存可见性,如程序顺序规则、锁规则、volatile 规则等。在实际项目中,我会遵循 JMM 规则,使用 volatile 保证可见性,使用 synchronized 保证原子性和可见性。"

深入解析:

  • 主内存:共享内存,存储所有变量
  • 工作内存:线程私有,存储变量副本
  • 内存可见性:线程间变量的可见性
  • happens-before:内存操作的顺序规则

22. 什么是内存可见性?如何保证内存可见性?

答案要点:

  • 内存可见性的概念
  • 可见性问题
  • 保证方法
  • 实际应用

示例答案: "内存可见性是指一个线程对共享变量的修改对其他线程可见。由于每个线程都有自己的工作内存,线程对变量的修改可能不会立即对其他线程可见,导致可见性问题。保证内存可见性的方法包括:使用 volatile 关键字,保证变量的修改立即对其他线程可见;使用 synchronized 关键字,保证同步块内的变量对其他线程可见;使用 final 关键字,保证 final 变量的初始化对其他线程可见。在实际项目中,我会使用 volatile 保证简单变量的可见性,使用 synchronized 保证复杂操作的可见性。"

深入解析:

  • 可见性问题:线程间变量修改不可见
  • volatile:保证变量修改的可见性
  • synchronized:保证同步块内变量的可见性
  • final:保证 final 变量初始化的可见性

并发编程最佳实践

23. 并发编程的最佳实践有哪些?

答案要点:

  • 线程安全设计
  • 性能优化
  • 错误处理
  • 调试技巧

示例答案: "并发编程的最佳实践包括:优先使用不可变对象,避免共享可变状态;使用线程安全的集合类,如 ConcurrentHashMap;合理使用锁,避免死锁和性能问题;使用线程池管理线程,避免频繁创建销毁线程;正确处理异常,避免线程异常导致程序崩溃;使用适当的同步机制,如 volatile、synchronized、Lock 等;避免过度同步,只在必要时使用同步;使用并发工具类,如 CountDownLatch、CyclicBarrier 等。在实际项目中,我会遵循这些最佳实践,编写高质量、高性能的并发程序。"

深入解析:

  • 线程安全:不可变对象、线程安全集合
  • 性能优化:合理使用锁、线程池
  • 错误处理:异常处理、资源清理
  • 调试技巧:线程转储、监控工具

24. 如何调试并发程序?

答案要点:

  • 调试工具
  • 线程转储
  • 死锁检测
  • 性能分析

示例答案: "调试并发程序需要特殊的工具和技巧。调试工具包括 jstack 用于生成线程转储,jconsole 用于监控线程状态,VisualVM 用于性能分析。线程转储可以显示所有线程的状态、锁等待关系、死锁信息等。死锁检测可以通过分析线程转储中的锁等待关系实现。性能分析可以使用 JProfiler、YourKit 等专业工具。在实际项目中,我会使用这些工具诊断并发问题,分析线程状态,检测死锁,优化性能。"

深入解析:

  • 调试工具:jstack、jconsole、VisualVM
  • 线程转储:分析线程状态和锁关系
  • 死锁检测:分析锁等待关系
  • 性能分析:使用专业工具分析性能

多线程与并发总结

核心要点回顾

  1. 线程基础:线程概念、创建方式、生命周期
  2. 线程同步:synchronized、volatile、死锁
  3. 锁机制:ReentrantLock、读写锁、条件变量
  4. 线程池:线程池配置、拒绝策略、监控
  5. 并发工具:CountDownLatch、CyclicBarrier、Semaphore
  6. 原子操作:原子类、CAS 操作
  7. 内存模型:JMM、内存可见性
  8. 最佳实践:线程安全、性能优化、调试

面试重点

  • 深入理解线程同步机制
  • 掌握各种锁的使用场景
  • 理解线程池的工作原理
  • 熟悉并发工具类的应用
  • 了解内存模型和可见性
  • 掌握并发编程最佳实践

常见陷阱

  • 忽略线程安全问题
  • 过度使用同步机制
  • 死锁和活锁问题
  • 内存可见性问题
  • 线程池配置不当

性能优化

  • 合理选择同步机制
  • 使用无锁并发
  • 优化线程池配置
  • 减少锁竞争
  • 使用并发工具类

注:本文档涵盖了 Java 多线程与并发的核心面试题,在实际面试中应结合具体代码示例和实际项目经验进行回答。建议通过实际编程练习加深理解。

最近更新:: 2025/9/4 10:51
Contributors: Duke
Prev
Java 集合框架面试题
Next
JVM 与内存管理面试题