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

【Java核心技术/多线程】35道Java多线程面试题与答案

1. 什么是线程?线程与进程的区别是什么?

  • 答案:线程是进程内的执行单元,是CPU调度的最小单位;进程是资源分配的最小单位,一个进程可包含多个线程。
  • 核心区别:进程间资源独立,线程共享进程的内存/文件句柄等资源,线程切换成本远低于进程。

2. Java中创建线程的3种方式是什么?

  • 答案:
    继承 Thread 类,重写 run() 方法;
    实现 Runnable 接口,重写 run() 方法,将实例传给 Thread ;
    实现 Callable 接口,重写 call() 方法(可返回结果、抛异常),结合 FutureTask 使用。

3. start() 方法和 run() 方法的区别?

  • 答案: start() 会启动新线程,由JVM调用 run() ;直接调用 run() 只是普通方法调用,不会创建新线程,仍在当前线程执行。

4. 线程的生命周期有哪些状态?

  • 答案:Java线程生命周期(6种状态,定义在 Thread.State 枚举中)
    NEW :线程创建后未启动;
    RUNNABLE :就绪(等待CPU)或运行中;
    BLOCKED :等待同步锁(如 synchronized 未获取到);
    WAITING :无超时等待(如 wait() 、 join() ),需被唤醒;
    TIMED_WAITING :有超时等待(如 wait(1000) 、 sleep(1000) );
    TERMINATED :线程执行完毕。

5. synchronized 关键字的作用是什么?有几种用法?

  • 答案:作用是保证多线程对共享资源的原子性、可见性、有序性(防止指令重排序),实现同步。
  • 用法:
    修饰普通方法:锁是当前对象实例;
    修饰静态方法:锁是当前类的Class对象;
    修饰代码块:锁是括号内的指定对象/类(如 synchronized (this) {} )。

6. synchronized 和 volatile 的区别?

  • 答案:
  • volatile :仅保证可见性和有序性,不保证原子性,用于修饰变量;
  • synchronized :保证原子性、可见性、有序性,可修饰方法/代码块,是重量级锁(JDK 1.6后优化为轻量级锁、偏向锁)。

7. volatile 的原理是什么?为什么能保证可见性?

  • 答案:原理是“内存屏障”:
    写 volatile 变量时,会在操作后加“写屏障”,强制将本地内存的修改刷到主内存;
    读 volatile 变量时,会在操作前加“读屏障”,强制从主内存加载最新值到本地内存;
    由此保证多线程间变量的可见性,且禁止指令重排序(如单例双重检查的 instance 需加 volatile )。

8. 什么是原子类?Java提供了哪些原子类?

  • 答案:原子类是通过CAS(Compare and Swap)实现无锁原子操作的类,避免 synchronized 的性能开销,位于 java.util.concurrent.atomic 包。
    常见原子类:
    基本类型: AtomicInteger 、 AtomicLong 、 AtomicBoolean ;
    引用类型: AtomicReference 、 AtomicStampedReference (解决ABA问题);
    数组类型: AtomicIntegerArray 、 AtomicLongArray 。

9. CAS是什么?有什么缺点?

  • 答案:CAS(Compare and Swap,比较并交换)是无锁同步机制,核心是“先比较内存值是否等于预期值,若相等则更新为新值,否则不操作”。
  • 缺点:
    ABA问题:内存值从A→B→A,CAS会误判为未修改(可通过 AtomicStampedReference 加版本号解决);
    自旋开销:CAS失败后会循环重试,高并发下占用CPU;
    只能保证单个变量原子性:无法实现多变量组合操作的原子性。

10. 什么是AQS?它的核心原理是什么?

  • 答案:AQS(AbstractQueuedSynchronizer,抽象队列同步器)是Java并发工具的基础框架(如 ReentrantLock 、 CountDownLatch ),用于实现锁和同步器。
  • 核心原理:
  • 维护一个volatile int state(同步状态,如0=未锁定,1=锁定)和一个双向阻塞队列(存储等待线程);
  • 子类通过重写 tryAcquire() (获取锁)、 tryRelease() (释放锁)等方法,控制 state 的修改;
  • 线程获取锁失败时,会被封装为节点加入队列,然后阻塞;释放锁时,唤醒队列头节点的线程。

11. ReentrantLock 是什么?它与 synchronized 的区别?

  • 答案: ReentrantLock 是JDK提供的可重入独占锁,基于AQS实现,需手动 lock() 和 unlock() (通常在 try-finally 中)。
  • 与 synchronized 的区别:
    维度 synchronized ReentrantLock
    锁获取/释放 自动(进入方法/代码块时获取,退出时释放) 手动(需显式调用 lock() / unlock() )
    可中断性 不可中断 可中断( lockInterruptibly() )
    超时获取 不支持 支持( tryLock(long timeout) )
    公平锁 非公平(默认),无法指定 可指定公平/非公平(构造函数传 true )
    条件变量 仅1个(通过 wait() / notify() ) 多个(通过 newCondition() 创建)

12. 什么是可重入锁? synchronized 和 ReentrantLock 是可重入的吗?

  • 答案:可重入锁(递归锁)指线程获取锁后,可再次获取同一把锁而不被阻塞(即锁会记录持有线程和重入次数,释放时次数减为0才真正释放)。
  • 是, synchronized 和 ReentrantLock 均为可重入锁。例如:一个 synchronized 方法调用另一个 synchronized 方法,同一线程不会死锁。

13. 什么是公平锁和非公平锁? ReentrantLock 默认是哪种?

  • 答案:
  • 公平锁:线程获取锁的顺序与请求顺序一致(先到先得),避免线程饥饿,但性能较低;
  • 非公平锁:线程获取锁时不按请求顺序,可“插队”(刚释放的锁可能被新请求的线程获取),性能高,但可能导致线程饥饿;
  • ReentrantLock 默认是非公平锁,构造函数传 true 可创建公平锁。

14. 什么是死锁?产生死锁的4个必要条件是什么?

  • 答案:死锁是多个线程互相持有对方需要的锁,且都不释放,导致所有线程永久阻塞的状态。
  • 4个必要条件(缺一不可):
  1. 互斥条件:资源只能被一个线程占用;
  2. 持有并等待:线程持有一个资源,同时等待另一个资源;
  3. 不可剥夺:线程已持有的资源不能被强制剥夺;
  4. 循环等待:多个线程形成“资源请求循环”(如A等B的锁,B等A的锁)。

15. 如何避免死锁?

  • 答案:破坏死锁的4个必要条件之一即可:
  1. 破坏“循环等待”:给所有锁按固定顺序编号,线程按编号顺序获取锁;
  2. 破坏“持有并等待”:一次性获取所有需要的锁,获取失败则释放已持有的锁;
  3. 破坏“不可剥夺”:使用 tryLock(timeout) ,超时则释放已持有的锁;
  4. 破坏“互斥条件”:使用可共享的资源(如读写锁的读锁)。

16. Thread.sleep(long millis) 和 Object.wait() 的区别?

  • 答案:
  • 所属类不同: sleep() 是 Thread 的静态方法; wait() 是 Object 的实例方法;
  • 锁释放不同: sleep() 不释放当前持有的锁; wait() 会释放锁,直到被 notify() / notifyAll() 唤醒;
  • 唤醒方式不同: sleep() 到时间自动唤醒; wait() 需显式唤醒(或超时自动唤醒);
  • 使用场景不同: sleep() 用于暂停执行; wait() 用于线程间通信(如生产者-消费者)。

17. notify() 和 notifyAll() 的区别?

  • 答案:
  • notify() :随机唤醒当前对象等待队列中的一个线程,使其进入就绪状态;
  • notifyAll() :唤醒当前对象等待队列中的所有线程,使其进入就绪状态;
  • 注意:两者都需在 synchronized 代码块/方法中调用,否则抛 IllegalMonitorStateException 。

18. 什么是线程池?为什么要用线程池?

  • 答案:线程池是管理线程的“容器”,预先创建一批线程,复用线程执行任务,避免频繁创建/销毁线程的开销。
  • 核心优势:
  1. 降低资源消耗:复用线程,减少线程创建/销毁的CPU和内存开销;
  2. 提高响应速度:任务到达时,无需等待线程创建,直接复用现有线程;
  3. 便于管理:统一控制线程数量、任务队列大小,避免线程过多导致系统资源耗尽。

19. Java线程池的核心参数有哪些?( ThreadPoolExecutor 的7个参数)

  • 答案: ThreadPoolExecutor 的构造函数核心参数(7个):
  1. corePoolSize :核心线程数(线程池长期维持的线程数量,即使空闲也不销毁);
  2. maximumPoolSize :最大线程数(线程池可创建的最多线程数);
  3. keepAliveTime :非核心线程的空闲存活时间(超过该时间则销毁非核心线程);
  4. unit : keepAliveTime 的时间单位(如 TimeUnit.SECONDS );
  5. workQueue :任务阻塞队列(核心线程满时,新任务放入队列等待);
  6. threadFactory :创建线程的工厂(可自定义线程名称、优先级等);
  7. handler :拒绝策略(线程池和队列都满时,如何处理新任务)。

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

  • 答案:Java默认提供4种拒绝策略(实现 RejectedExecutionHandler 接口):
  1. AbortPolicy (默认):直接抛 RejectedExecutionException ,拒绝任务;
  2. CallerRunsPolicy :由提交任务的调用者线程执行任务(避免任务丢失,缓解线程池压力);
  3. DiscardPolicy :默默丢弃新任务,不抛异常;
  4. DiscardOldestPolicy :丢弃队列中最旧的任务(队列头部任务),然后尝试提交新任务。

21. 线程池的任务执行流程是什么?

  • 答案:线程池处理新任务的步骤(核心逻辑):
  1. 若当前线程数 < corePoolSize :创建核心线程执行任务;
  2. 若当前线程数 ≥ corePoolSize :检查任务队列 workQueue ,若队列未满,将任务放入队列;
  3. 若队列已满:检查当前线程数 < maximumPoolSize ,创建非核心线程执行任务;
  4. 若当前线程数 ≥ maximumPoolSize :触发拒绝策略处理任务。

22. Executors 提供的常见线程池有哪些?为什么不推荐使用?

  • 答案: Executors 是线程池工具类,提供4种预设线程池:
  1. newFixedThreadPool(n) :固定核心线程数的线程池( corePoolSize=maximumPoolSize=n ,队列无界);
  2. newSingleThreadExecutor() :单线程线程池( corePoolSize=maximumPoolSize=1 ,队列无界);
  3. newCachedThreadPool() :缓存线程池( corePoolSize=0 , maximumPoolSize=Integer.MAX_VALUE ,队列同步移交);
  4. newScheduledThreadPool(n) :定时任务线程池(核心线程数 n ,支持定时/周期性任务);
  • 不推荐原因:
  • FixedThreadPool / SingleThreadExecutor 的队列是无界的( LinkedBlockingQueue ),任务过多时会导致OOM;
  • CachedThreadPool 的最大线程数是 Integer.MAX_VALUE ,高并发下会创建大量线程,导致OOM。
    推荐:直接使用 ThreadPoolExecutor 手动指定参数,明确线程数和队列大小,避免资源耗尽。

23. 什么是读写锁? ReentrantReadWriteLock 的特点是什么?

  • 答案:读写锁是分离“读操作”和“写操作”的锁,允许多个线程同时读,仅允许一个线程写,提高读多写少场景的并发性能。
  • ReentrantReadWriteLock 的特点:
  1. 可重入:读线程可再次获取读锁,写线程可再次获取写锁/读锁;
  2. 公平/非公平:支持公平锁(构造函数传 true )和非公平锁(默认);
  3. 写锁独占:写锁被获取时,其他线程无法获取读锁/写锁;
  4. 读锁共享:读锁被获取时,其他线程可获取读锁,但无法获取写锁;
  5. 降级:写锁可降级为读锁(写线程获取读锁后释放写锁);
  6. 不可升级:读锁不可升级为写锁(避免死锁)。

24. 什么是CountDownLatch?它的使用场景是什么?

  • 答案: CountDownLatch 是基于AQS的同步工具,通过“计数器”实现:主线程等待多个子线程完成任务后,再继续执行。
  • 核心逻辑:
  • 初始化时指定计数器值(如 new CountDownLatch(3) 表示等待3个任务);
  • 子线程完成任务后调用 countDown() ,计数器减1;
  • 主线程调用 await() ,阻塞直到计数器变为0;
  • 使用场景:如主线程等待多个子线程加载资源完成后,再执行后续逻辑。

25. 什么是CyclicBarrier?它与CountDownLatch的区别?

  • 答案: CyclicBarrier (循环屏障)是基于AQS的同步工具,让多个线程到达“屏障点”后,再一起继续执行,且屏障可重复使用。
  • 与 CountDownLatch 的区别:
    维度 CountDownLatch CyclicBarrier
    计数器是否可重置 不可重置(计数器到0后失效) 可重置( reset() 方法,屏障可循环使用)
    线程角色 分“等待线程”和“计数线程”(主线程等子线程) 所有线程都是“等待线程”(线程间互相等待)
    触发条件 计数器到0 到达屏障的线程数达到预设值
    用途 1个线程等多个线程完成 多个线程互相等待,协同执行

26. 什么是Semaphore?它的使用场景是什么?

  • 答案: Semaphore (信号量)是基于AQS的同步工具,通过“许可数”控制同时访问共享资源的线程数量。
  • 核心逻辑:
  • 初始化时指定许可数(如 new Semaphore(5) 表示允许5个线程同时访问);
  • 线程获取资源前调用 acquire() ,获取1个许可(许可数减1,无许可则阻塞);
  • 线程释放资源后调用 release() ,释放1个许可(许可数加1);
  • 使用场景:限流(如控制同时访问数据库的连接数)、实现线程池等。

27. 什么是ThreadLocal?它的原理是什么?

  • 答案: ThreadLocal 是线程本地变量,让每个线程拥有独立的变量副本,避免多线程共享变量的并发问题。
  • 原理:
  • 每个 Thread 对象内部维护一个 ThreadLocalMap (键: ThreadLocal 实例,值:变量副本);
  • 线程调用 ThreadLocal.set(value) 时,将 value 存入当前线程的 ThreadLocalMap ;
  • 线程调用 ThreadLocal.get() 时,从当前线程的 ThreadLocalMap 中获取值;
  • 线程销毁时, ThreadLocalMap 也会销毁,避免内存泄漏(但需注意:若线程复用,需手动 remove() ,否则副本会残留)。

28. ThreadLocal可能导致内存泄漏的原因是什么?如何避免?

  • 答案:内存泄漏原因: ThreadLocalMap 的键是 ThreadLocal 的弱引用,值是强引用。当 ThreadLocal 实例被回收(弱引用失效),键变为 null ,但值仍被 ThreadLocalMap 引用,若线程长期存活(如线程池复用),值无法被GC回收,导致内存泄漏。
  • 避免方式:
  1. 使用完 ThreadLocal 后,手动调用 remove() 方法,清除 ThreadLocalMap 中的键值对;
  2. 避免使用静态 ThreadLocal (静态变量生命周期长,更易导致泄漏)。

29. 什么是线程安全?如何保证线程安全?

  • 答案:线程安全指多线程并发访问共享资源时,程序的执行结果与单线程执行结果一致,且无数据损坏、死锁等问题。
  • 保证方式:
  1. 锁机制: synchronized 、 ReentrantLock 、读写锁等;
  2. 无锁机制:原子类( AtomicInteger )、CAS;
  3. 线程本地变量: ThreadLocal (避免共享变量);
  4. 不可变对象:使用 final 修饰变量(对象一旦创建,状态不可修改,天然线程安全)。

30. 什么是不可变对象?为什么不可变对象是线程安全的?

  • 答案:不可变对象指对象创建后,其状态(成员变量的值)无法修改的对象(如 String 、 Integer )。
  • 线程安全原因:不可变对象的状态不会被修改,多线程访问时无需担心“一个线程修改,另一个线程读取”的并发问题,因此天然线程安全。

31. 什么是线程间通信?Java中实现线程间通信的方式有哪些?

  • 答案:线程间通信指多个线程通过共享资源或特定机制,协调执行顺序、传递数据的过程。
  • 实现方式:
  1. synchronized + wait() / notify() / notifyAll() ;
  2. ReentrantLock + Condition ( await() / signal() / signalAll() );
  3. 管道流: PipedInputStream / PipedOutputStream (线程间传递字节流);
  4. 共享变量: volatile 修饰的变量(保证可见性,实现简单通信);
  5. 同步工具: CountDownLatch 、 CyclicBarrier (协调线程执行顺序)。

32. Condition 接口的作用是什么?它与 wait() / notify() 的区别?

  • 答案: Condition 是 ReentrantLock 的条件变量,用于实现线程间的精准通信,可类比 synchronized 中的 wait() / notify() 。
  • 与 wait() / notify() 的区别:
  • 条件变量数量:一个 ReentrantLock 可创建多个 Condition (实现多条件等待); synchronized 仅对应一个条件变量(对象本身);
  • 唤醒精度: Condition 的 signal() 可唤醒指定条件队列的线程; notify() 只能随机唤醒一个线程;
  • 功能丰富度: Condition 支持 awaitUninterruptibly() (不可中断等待)、 awaitUntil(Date) (截止时间等待), wait() 无此功能。

33. 什么是线程的中断?如何中断一个线程?

  • 答案:线程中断是向线程发送“中断信号”,通知线程需要停止执行,但线程是否停止由自身决定(不会强制终止)。
  • 中断相关方法:
  1. thread.interrupt() :给线程发送中断信号,设置线程的“中断状态”为 true ;
  2. Thread.currentThread().isInterrupted() :判断当前线程的中断状态(不会清除状态);
  3. Thread.interrupted() :判断当前线程的中断状态(会清除状态,将状态重置为 false );
  • 注意:若线程处于 WAITING / TIMED_WAITING 状态,调用 interrupt() 会使线程抛出 InterruptedException ,并清除中断状态。

34. 什么是Fork/Join框架?它的核心思想是什么?

  • 答案:Fork/Join框架是Java 7引入的并行计算框架,基于“分治法”,将大任务拆分为多个小任务并行执行,最后合并小任务的结果。
  • 核心思想:
  1. Fork(拆分):将大任务拆分为多个可并行执行的小任务;
  2. Join(合并):等待所有小任务执行完毕,合并小任务的结果,得到大任务的最终结果;
  • 核心类: ForkJoinPool (线程池)、 ForkJoinTask (任务抽象类,子类 RecursiveTask 有返回值, RecursiveAction 无返回值)。

35. 什么是CompletableFuture?它的作用是什么?

  • 答案: CompletableFuture 是Java 8引入的异步编程工具,基于Future框架增强,支持链式调用、异步回调、任务组合,解决了传统 Future 需阻塞等待结果的问题。
  • 核心作用:
  1. 异步执行任务:无需手动创建线程,可指定线程池(默认用 ForkJoinPool.commonPool() );
  2. 链式回调:通过 thenApply() / thenAccept() / thenRun() 等方法,在任务完成后自动执行回调逻辑;
  3. 任务组合:通过 thenCombine() / allOf() / anyOf() 等方法,组合多个异步任务的结果;
  4. 异常处理:通过 exceptionally() / handle() 方法,优雅处理异步任务的异常。
http://www.dtcms.com/a/449187.html

相关文章:

  • 【AI智能体】Coze 打造AI数字人视频生成智能体实战详解
  • 网站开发外键邯郸网站开发定制
  • FreeRTOS任务同步与通信--事件标志组
  • Excel基础知识 - 导图笔记
  • Flink 执行模式在 STREAMING 与 BATCH 之间做出正确选择
  • 杭州网站制作平台公司医院网站建设存在问题
  • Python中*args与**kwargs用法解析
  • 【大模型】多智能体架构详解:Context 数据流与工作流编排的艺术
  • 描述逻辑(Description Logic)对自然语言处理深层语义分析的影响与启示
  • python爬虫(三) ---- 分页抓取数据
  • 探索大语言模型(LLM):大模型微调方式全解析
  • 【学习笔记03】C++STL标准模板库核心技术详解
  • 做网站有什么关于财务的问题网络设计工作
  • P9751 [CSP-J 2023] 旅游巴士
  • 宠物用品网站开发背景网站推广设计
  • MySql复习及面试题学习
  • .NET周刊【9月第2期 2025-09-14】
  • 秦皇岛企业网站建设wordpress 悬浮音乐
  • 日语学习-日语知识点小记-进阶-JLPT-N1阶段应用练习(6):语法 +考え方19+2022年7月N1
  • 【Linux指南】gdb进阶技巧:断点高级玩法与变量跟踪实战
  • 跨平台游戏引擎 Axmol-2.9.0 发布
  • 金融 - neo4j、Graph Data Science 安装
  • c 可以做网站吗梧州seo排名
  • LuaC API知识点汇总
  • mysql学习--DCL
  • 开源 C++ QT QML 开发(七)自定义控件--仪表盘
  • 论坛开源网站源码网站建设实验总结报告
  • Ansible实战:VMware下K8s自动化部署指南
  • Ansible(三)—— 使用Ansible自动化部署LNMP环境实战指南
  • 【深度学习新浪潮】有没有可能设计出一种统一架构,可以同时处理图像理解的各种下游任务?