【面试题系列】Java 多线程面试题深度解析
本文涉及Java 多线程面试题,从基础到高级,希望对你有所帮助!
一、基础概念类
1. 请简述 Java 中线程的几种状态及其转换条件
题目分析:这是多线程基础中的基础,考查对线程生命周期的理解,在多线程编程中,线程状态的转换是核心机制之一。
答案:
Java 中线程有六种状态,定义在 Thread.State
枚举中:
- NEW(新建):线程被创建但还未调用
start()
方法。例如:
Thread thread = new Thread(() -> System.out.println("Running"));
// 此时 thread 处于 NEW 状态
- RUNNABLE(可运行):线程调用
start()
方法后进入该状态,它可能正在运行,也可能在等待 CPU 时间片。 - BLOCKED(阻塞):线程在等待获取一个排它锁(synchronized 同步块)时进入该状态,当锁被释放且该线程竞争到锁时,会转换回 RUNNABLE 状态。
public class BlockedExample {private static final Object lock = new Object();public static void main(String[] args) {Thread t1 = new Thread(() -> {synchronized (lock) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});Thread t2 = new Thread(() -> {synchronized (lock) {System.out.println("t2 got the lock");}});t1.start();t2.start();// t2 可能会进入 BLOCKED 状态等待 lock}
}
- WAITING(等待):线程调用
Object.wait()
、Thread.join()
或LockSupport.park()
方法后进入该状态,需要其他线程调用Object.notify()
、Object.notifyAll()
或LockSupport.unpark()
来唤醒。 - TIMED_WAITING(计时等待):与 WAITING 类似,但有时间限制,例如调用
Thread.sleep(long millis)
、Object.wait(long timeout)
等方法。 - TERMINATED(终止):线程执行完毕或者因异常退出。
2. 什么是守护线程?有什么作用?
题目分析:守护线程是 Java 线程机制中的一个特殊概念,考查对线程不同类型及其用途的理解。
答案:
守护线程是一种特殊的线程,它的作用是为其他线程提供服务。当所有非守护线程结束时,守护线程会自动终止,即使它的任务还未完成。
在 Java 中,可以通过 setDaemon(true)
方法将线程设置为守护线程,且该方法必须在 start()
方法之前调用。例如:
Thread daemonThread = new Thread(() -> {while (true) {try {Thread.sleep(1000);System.out.println("Daemon thread is running");} catch (InterruptedException e) {e.printStackTrace();}}
});
daemonThread.setDaemon(true);
daemonThread.start();
守护线程常用于垃圾回收、监控等服务,比如 JVM 的垃圾回收线程就是一个典型的守护线程。
二、同步与锁类
1. 请比较 synchronized
和 ReentrantLock
的异同
题目分析:这是多线程同步机制中的重点内容,synchronized
和 ReentrantLock
是常用的同步手段,考查对它们的理解和使用场景的掌握。
答案:
相同点:
- 都用于实现线程同步,保证同一时间只有一个线程可以访问共享资源。
- 都具有可重入性,即同一个线程可以多次获取同一把锁而不会发生死锁。
不同点:
- 语法层面:
synchronized
是 Java 关键字,是内置的语言实现;ReentrantLock
是一个类,需要手动调用lock()
和unlock()
方法来加锁和解锁。
// synchronized 示例
public class SynchronizedExample {private int count = 0;public synchronized void increment() {count++;}
}// ReentrantLock 示例
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private int count = 0;private final ReentrantLock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}
}
- 灵活性:
ReentrantLock
更加灵活,例如可以实现公平锁(new ReentrantLock(true)
),还可以使用tryLock()
方法尝试获取锁,避免线程长时间阻塞。 - 锁的释放:
synchronized
会在同步块或方法执行完毕后自动释放锁;ReentrantLock
必须在finally
块中手动调用unlock()
方法释放锁,否则可能导致死锁。
2. 什么是死锁?如何避免死锁?
题目分析:死锁是多线程编程中常见且严重的问题,考查对死锁概念和解决方法的掌握。
答案:
死锁是指两个或多个线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
死锁的产生需要满足四个必要条件:
- 互斥条件:进程对所分配到的资源进行排他性使用,即在一段时间内某资源只由一个进程占用。
- 请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
- 不剥夺条件:进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
- 循环等待条件:在发生死锁时,必然存在一个进程——资源的环形链,即进程集合 {P0,P1,P2,···,Pn} 中的 P0 正在等待一个 P1 占用的资源;P1 正在等待 P2 占用的资源,……,Pn 正在等待已被 P0 占用的资源。
避免死锁的方法有:
- 破坏请求和保持条件:可以采用资源一次性分配的策略,即进程在运行前一次性申请它所需要的全部资源,在它的资源未满足前,不把它投入运行。
- 破坏不剥夺条件:允许进程剥夺使用其它进程占有的资源。
- 破坏循环等待条件:采用资源有序分配法,即把系统中的所有资源编号,进程在请求资源时,必须严格按资源编号的递增顺序进行,避免形成资源的环形链。
- 使用定时锁:例如
ReentrantLock
的tryLock(long timeout, TimeUnit unit)
方法,在一定时间内无法获取锁时,线程可以放弃等待,避免死锁。
三、线程池类
1. 请简述 Java 中线程池的工作原理和主要参数
题目分析:线程池是 Java 多线程编程中的重要工具,考查对线程池内部机制和参数的理解。
答案:
工作原理:
线程池的核心思想是预先创建一定数量的线程,当有任务提交时,从线程池中获取线程来执行任务,任务执行完毕后线程不会销毁,而是返回线程池等待下一个任务。这样可以避免频繁创建和销毁线程带来的性能开销。
线程池的主要工作流程如下:
- 当有新任务提交时,首先检查线程池中的核心线程数是否达到
corePoolSize
,如果未达到,则创建新的核心线程来执行任务。 - 如果核心线程数已达到
corePoolSize
,则将任务放入阻塞队列中。 - 如果阻塞队列已满,且线程池中的线程数未达到
maximumPoolSize
,则创建新的非核心线程来执行任务。 - 如果线程池中的线程数已达到
maximumPoolSize
,且阻塞队列已满,则根据拒绝策略来处理新任务。
主要参数:
- corePoolSize:核心线程数,线程池始终保持的线程数量。
- maximumPoolSize:线程池允许的最大线程数。
- keepAliveTime:非核心线程在空闲时的存活时间,超过该时间线程将被销毁。
- TimeUnit:
keepAliveTime
的时间单位。 - BlockingQueue:阻塞队列,用于存储等待执行的任务。常见的阻塞队列有
ArrayBlockingQueue
、LinkedBlockingQueue
等。 - ThreadFactory:线程工厂,用于创建线程。
- RejectedExecutionHandler:拒绝策略,当线程池和阻塞队列都已满时,如何处理新提交的任务。常见的拒绝策略有
AbortPolicy
(直接抛出异常)、CallerRunsPolicy
(由调用者线程执行任务)等。
2. 如何合理配置线程池的参数?
题目分析:这是线程池使用中的关键问题,合理配置参数可以提高线程池的性能和稳定性。
答案:
线程池参数的配置需要根据具体的业务场景和系统资源来决定,以下是一些参考原则:
- corePoolSize:
- 对于 CPU 密集型任务(如计算、加密等),线程池的核心线程数可以设置为 CPU 核心数 + 1,这样可以充分利用 CPU 资源,避免线程上下文切换带来的开销。可以使用
Runtime.getRuntime().availableProcessors()
方法获取 CPU 核心数。 - 对于 I/O 密集型任务(如文件读写、网络请求等),线程池的核心线程数可以设置得大一些,一般可以设置为 CPU 核心数 * 2,因为 I/O 操作会使线程阻塞,此时可以让其他线程继续执行任务。
- 对于 CPU 密集型任务(如计算、加密等),线程池的核心线程数可以设置为 CPU 核心数 + 1,这样可以充分利用 CPU 资源,避免线程上下文切换带来的开销。可以使用
- maximumPoolSize:
- 一般情况下,
maximumPoolSize
可以设置为比corePoolSize
大一些,以应对突发的任务高峰。但也不宜设置得过大,否则会占用过多的系统资源。
- 一般情况下,
- BlockingQueue:
- 对于任务执行时间较短、任务数量较多的场景,可以使用有界队列(如
ArrayBlockingQueue
),避免队列无限增长导致内存溢出。 - 对于任务执行时间较长、任务数量较少的场景,可以使用无界队列(如
LinkedBlockingQueue
),让任务在队列中等待执行。
- 对于任务执行时间较短、任务数量较多的场景,可以使用有界队列(如
- keepAliveTime:
- 可以根据任务的执行频率和系统资源情况来设置,一般可以设置为几十秒到几分钟不等。如果任务执行频率较高,可以适当缩短
keepAliveTime
;如果任务执行频率较低,可以适当延长keepAliveTime
。
- 可以根据任务的执行频率和系统资源情况来设置,一般可以设置为几十秒到几分钟不等。如果任务执行频率较高,可以适当缩短
- RejectedExecutionHandler:
- 根据业务需求选择合适的拒绝策略。如果对任务丢失不敏感,可以选择
AbortPolicy
;如果希望调用者线程来执行任务,可以选择CallerRunsPolicy
。
- 根据业务需求选择合适的拒绝策略。如果对任务丢失不敏感,可以选择
四、并发工具类类
1. 请简述 CountDownLatch
和 CyclicBarrier
的区别和使用场景
题目分析:CountDownLatch
和 CyclicBarrier
是 Java 并发包中常用的同步工具,考查对它们的功能和使用场景的理解。
答案:
区别:
- 计数机制:
CountDownLatch
的计数器是递减的,初始值为需要等待的线程数量,每个线程完成任务后调用countDown()
方法将计数器减 1,当计数器为 0 时,等待的线程可以继续执行;CyclicBarrier
的计数器是递增的,初始值为需要等待的线程数量,每个线程到达屏障时调用await()
方法,当计数器达到初始值时,所有等待的线程同时继续执行,并且计数器可以重置,重复使用。 - 使用方式:
CountDownLatch
主要用于一个或多个线程等待其他线程完成任务;CyclicBarrier
主要用于多个线程相互等待,达到一个共同的屏障点后再继续执行。
使用场景:
- CountDownLatch:适用于一个主线程等待多个子线程完成任务的场景,例如在多线程下载中,主线程等待所有子线程下载完成后进行合并操作。
import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) throws InterruptedException {int threadCount = 3;CountDownLatch latch = new CountDownLatch(threadCount);for (int i = 0; i < threadCount; i++) {new Thread(() -> {try {System.out.println(Thread.currentThread().getName() + " is working");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {latch.countDown();}}).start();}latch.await();System.out.println("All threads have finished their work");}
}
- CyclicBarrier:适用于多个线程需要同步到某个点后再继续执行的场景,例如多个运动员在起跑线等待发令枪响后同时起跑。
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;public class CyclicBarrierExample {public static void main(String[] args) {int threadCount = 3;CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> System.out.println("All threads have reached the barrier"));for (int i = 0; i < threadCount; i++) {new Thread(() -> {try {System.out.println(Thread.currentThread().getName() + " is waiting at the barrier");barrier.await();System.out.println(Thread.currentThread().getName() + " has passed the barrier");} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}}).start();}}
}
2. Semaphore
有什么作用?请举例说明
题目分析:Semaphore
是 Java 并发包中用于控制并发访问数量的工具,考查对其功能和使用场景的理解。
答案:
Semaphore
可以理解为一个信号量,它用于控制同时访问某个资源的线程数量。Semaphore
内部维护了一个计数器,线程在访问资源前需要先获取信号量(调用 acquire()
方法),计数器减 1;线程访问完资源后需要释放信号量(调用 release()
方法),计数器加 1。当计数器为 0 时,其他线程需要等待,直到有线程释放信号量。
使用场景包括限制并发访问资源的数量,例如数据库连接池、限流等。
以下是一个简单的示例,模拟多个线程同时访问有限的资源:
import java.util.concurrent.Semaphore;public class SemaphoreExample {private static final int RESOURCE_COUNT = 3;private static final int THREAD_COUNT = 5;private static final Semaphore semaphore = new Semaphore(RESOURCE_COUNT);public static void main(String[] args) {for (int i = 0; i < THREAD_COUNT; i++) {new Thread(() -> {try {// 获取信号量semaphore.acquire();System.out.println(Thread.currentThread().getName() + " has acquired the resource");// 模拟使用资源Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();} finally {// 释放信号量semaphore.release();System.out.println(Thread.currentThread().getName() + " has released the resource");}}).start();}}
}
在这个示例中,有 5 个线程尝试访问 3 个资源,通过 Semaphore
可以控制同时只有 3 个线程可以访问资源,其他线程需要等待。
以上面试题涵盖了 Java 多线程的多个方面,从基础概念到高级应用,对于 Java
高级研发工程师来说,需要深入理解并熟练掌握这些知识,才能在多线程编程中应对各种复杂的场景。
相关文章:

【面试题系列】Java 多线程面试题深度解析
本文涉及Java 多线程面试题,从基础到高级,希望对你有所帮助! 一、基础概念类 1. 请简述 Java 中线程的几种状态及其转换条件 题目分析:这是多线程基础中的基础,考查对线程生命周期的理解,在多线程编程中&…...

【C语言】左旋字符串(三种实现方式)
题目: 实现一个函数,可以左旋字符串中的k个字符。 例如: ABCD左旋一个字符得到BCDA ABCD左旋两个字符得到CDAB 方法一: 我们画个图分析一下: 基本逻辑: 就是我们每一次旋转之前,我们就取出…...

数论补充 之 前后缀分解问题
文章目录 [0,i-1] 和 [i1,n-1] 共同作用3334,数组的最大因子得分 对于前缀分解问题,我愿把它分为几个大问题:[0,i] 或 [i,n-1] 或 [l,r],或 [0,i-1] 和 [i1,n-1] 共同作用的问题 [0,i-1] 和 [i1,n-1] 共同作用 3334,数组的最大因子得分 3334,数组的最大…...
IoTDB 集群节点 IP 改变,如何更新集群
问题 问题1:如果 IoTDB 配置的时候用的 IP,没有用 hostname,后面 IP 修改了,历史数据需要重新导吗? 问题2:如果现场运行 IoTDB 半年,电脑 IP 要改的话,半年的数据要导出来再导入么…...
【AI系列】从零开始学习大模型GPT (2)- Build a Large Language Model (From Scratch)
前序文章 【AI系列】从零开始学习大模型GPT (1)- Build a Large Language Model (From Scratch) Build a Large Language Model 背景第1章:理解大型语言模型第2章:处理文本数据第3章:编码Attention机制什么是Attention机制?Attention机制的基本原理数学表示应用总结为什么要…...

webshell通信流量分析
环境安装 Apatche2 php sudo apt install apache2 -y sudo apt install php libapache2-mod-php php-mysql -y echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/info.php sudo ufw allow Apache Full 如果成功访问info.php,则环境安…...

数据可视化+SpringBoot+协同过滤推荐算法的美食点餐管理平台
感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,我会一一回复的,希望帮助到更多的人。 背景分析 在当今数字化浪潮席卷全球的时代,餐饮行业也正经历着深刻的变革…...
DeepSeek 关联 Word 使用教程:解锁办公新效率
在当今数字化办公时代,将强大的人工智能模型与常用办公软件相结合,能显著提升工作效率。DeepSeek 作为一款先进的人工智能工具,若能与广泛使用的办公软件 Word 实现关联,可在文档撰写、编辑、内容优化等诸多方面为用户带来极大便利…...

[极客大挑战 2019]Havefun1
[极客大挑战 2019]Havefun1 代码审计发现 根据代码逻辑,要求传入’cat’参数,值为’dog’时执行if的操作,所以构造参数: ?catdog获得flag...

基于Swift实现仿IOS闹钟
仿 iOS 系统闹钟 添加闹钟效果图 收到通知效果图 更新日志 2018.09.12 由于 iOS 系统限制了注册本地推送的数量,最大的注册量为 64 条,且一旦超出 64 条,所有的推送都将失效,故而在添加推送的时候做了一个判断,超过…...

Threadlocal的实现原理
文章目录 ThreadLocal与Thread关系分析Threadlocal 不支持继承性lnheritableThreadLocal 类 ThreadLocal与Thread关系分析 由该图可知, Thread 类中有一个 threadLocals 和一个 inheritableThreadLocals , 它们 都是 ThreadLocalMap 类型 的变量 &#x…...

线程池处理异常
线程池在提交的任务在处理过程中发生了异常,却没有捕获到,导致异常只是输出在控制台,这通常需要把异常记录下来1、通过观察ThreadGroup的构造方法知道,当调用线程组的构造方法时,会获取当前线程所属的线程组࿰…...

RabbitMQ配置SSL证书
配置阿里云服务器RabbitMQ-SSL证书【windows】 文章目录 配置阿里云服务器RabbitMQ-SSL证书【windows】1. 证书下载2. 系统中添加证书(不知道是不是必要的)3. OpenSSL下载4. ca、server证书及私钥提取5. RabbitMQ-SSL证书配置6. 参考博客 1. 证书下载 进…...
.NET 9.0 的 Blazor Web App 项目,进度条 <progress> 组件使用注意事项
一、执行过程中,要刷新 进度条 的显示,需要 延时、释放,否则进度条不 实时 更新,最后一下到 100% // 延时,释放给前端:【必须】,否则进度条不 实时 更新,最后一下到 100await Task.D…...
第J7周:对于ResNeXt-50算法的思考
目录 FROM思考 FROM 🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 📌你需要解决的疑问:这个代码是否有错?对错与否都请给出你的思考 📌打卡要求:…...

【第2章:神经网络基础与实现——2.3 多层感知机(MLP)的构建与调优技巧】
在当今科技飞速发展的时代,人工智能早已不是一个陌生的词汇,它已经渗透到我们生活的方方面面,从智能语音助手到自动驾驶汽车,从图像识别到自然语言处理。而支撑这一切的核心技术之一,就是神经网络。作为机器学习领域的璀璨明星,神经网络已经在众多任务中取得了令人瞩目的…...
【Elasticsearch】keyword分析器
Elasticsearch 中的keyword分析器是一种非常特殊的分析器,它的行为与其他常见的分析器(如standard、whitespace等)截然不同。keyword分析器的核心功能是将整个输入字符串作为一个单一的标记(token)返回,而不…...

重生之我在异世界学编程之C语言:深入预处理篇(上)目录)
大家好,这里是小编的博客频道 小编的博客:就爱学编程 很高兴在CSDN这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!! 本文目录 引言正文一、预处理的作用与流程…...

MySQL数据库误删恢复_mysql 数据 误删
2、BigLog日志相关 2.1、检查biglog状态是否开启 声明: 当前为mysql版本5.7 当前为mysql版本5.7****当前为mysql版本5.7 2.1.1、Navicat工具执行 SHOW VARIABLES LIKE LOG_BIN%;OFF 是未开启状态,如果不是ON 开启状态需要开启为ON。{默认情况下就是关闭状态} 2.…...
SpringAI集成DeepSeek实战
SpringAI集成DeepSeek实战教程 引言 Spring AI作为Spring生态系统中的新成员,为开发者提供了便捷的AI集成方案。本文将详细介绍如何在Spring项目中集成DeepSeek模型,实现智能对话等功能。 环境准备 在开始之前,请确保您的开发环境满足以下要…...

UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...

群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...

实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...

渗透实战PortSwigger靶场:lab13存储型DOM XSS详解
进来是需要留言的,先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码,输入的<>当成字符串处理回显到页面中,看来只是把用户输…...

针对药品仓库的效期管理问题,如何利用WMS系统“破局”
案例: 某医药分销企业,主要经营各类药品的批发与零售。由于药品的特殊性,效期管理至关重要,但该企业一直面临效期问题的困扰。在未使用WMS系统之前,其药品入库、存储、出库等环节的效期管理主要依赖人工记录与检查。库…...