Java 常见的多线程面试题
在Java开发中,多线程编程是一个非常重要的技能,也是面试中的常见考点。本文将围绕一些经典的多线程面试题展开,从基础概念到高级应用,帮助你深入理解Java多线程的核心原理与实践。
1. 多线程的基本概念
什么是线程?线程与进程有什么区别?
线程是CPU调度的最小单位,而进程是操作系统资源分配的最小单位。一个进程可以包含多个线程,这些线程共享进程的内存空间和资源。
线程的生命周期包括哪些状态?
线程生命周期包括以下状态:
- 新建(New):线程对象被创建,但尚未启动。
- 可运行(Runnable):线程被启动,等待CPU调度。
- 运行(Running):线程正在执行。
- 阻塞(Blocked):线程因等待资源而暂停。
- 终止(Terminated):线程执行结束。
2. 线程创建与启动
Java中创建线程的方式有哪些?
-
继承
Thread
类:class MyThread extends Thread { @Override public void run() { System.out.println("Thread is running"); } } new MyThread().start();
-
实现
Runnable
接口:class MyRunnable implements Runnable { @Override public void run() { System.out.println("Thread is running"); } } new Thread(new MyRunnable()).start();
-
使用
Callable
和Future
:import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; class MyCallable implements Callable<String> { @Override public String call() { return "Thread is running"; } } FutureTask<String> task = new FutureTask<>(new MyCallable()); new Thread(task).start(); System.out.println(task.get());
3. 线程安全与同步
如何保证线程安全?
线程安全是指多线程环境下,代码的执行结果与单线程环境下结果一致。常见的解决方案包括:
-
使用
synchronized
关键字:- 修饰方法:
public synchronized void method() { // Critical section }
- 修饰代码块:
public void method() { synchronized (this) { // Critical section } }
- 修饰方法:
-
使用
Lock
接口:import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; Lock lock = new ReentrantLock(); lock.lock(); try { // Critical section } finally { lock.unlock(); }
-
使用线程安全的集合类:
Java提供了如ConcurrentHashMap
、CopyOnWriteArrayList
等线程安全集合类。
死锁是什么?如何避免?
死锁是指两个或多个线程相互等待对方释放资源,导致所有线程都无法继续执行。避免死锁的方法:
- 避免嵌套锁。
- 按统一顺序获取锁。
- 使用尝试锁(如
tryLock
方法)。
4. 线程间通信
线程间如何通信?
-
使用
wait
、notify
和notifyAll
:class SharedResource { private boolean flag = false; public synchronized void produce() throws InterruptedException { while (flag) { wait(); } System.out.println("Producing..."); flag = true; notify(); } public synchronized void consume() throws InterruptedException { while (!flag) { wait(); } System.out.println("Consuming..."); flag = false; notify(); } }
-
使用
BlockingQueue
:import java.util.concurrent.BlockingQueue; import java.util.concurrent.ArrayBlockingQueue; BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10); // Producer new Thread(() -> { try { queue.put(1); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); // Consumer new Thread(() -> { try { System.out.println(queue.take()); } catch (InterruptedException e) { e.printStackTrace(); } }).start();
5. 高级多线程知识
什么是线程池?如何使用线程池?
线程池通过复用线程来减少频繁创建和销毁线程的开销。Java通过ExecutorService
接口提供线程池支持。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
executor.execute(() -> {
System.out.println("Thread is running");
});
}
executor.shutdown();
volatile
关键字的作用是什么?
- 保证变量的可见性:一个线程修改了变量,其他线程可以立即看到。
- 禁止指令重排序。
什么是乐观锁与悲观锁?
- 悲观锁:认为并发操作会发生冲突,通过加锁避免冲突。
- 乐观锁:认为并发操作通常不会冲突,通过版本号或CAS机制检测冲突。
6. 经典面试题
-
写一个线程安全的单例模式:
class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
-
用Java实现生产者-消费者模型:
详见BlockingQueue
的实现部分。 -
解释线程的上下文切换:
线程上下文切换是指CPU从一个线程切换到另一个线程时保存和恢复线程状态的过程,可能引起性能开销。
7. 总结
Java多线程是一个宽泛而深入的领域,面试时需要既掌握基础概念又能够处理实际问题。通过以上的经典问题和代码示例,相信你能更好地准备多线程相关的面试。
希望这篇博客对你有所帮助!如果你有其他Java多线程问题,欢迎留言交流。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 2 本地部署DeepSeek模型构建本地知识库+联网搜索详细步骤