Java后端面试必看|多线程基础(Thread/Runnable/线程状态)+ 实战,小白也能懂

Java后端面试必看|多线程基础(Thread/Runnable/线程状态)+ 实战,小白也能懂 作为Java后端程序员「多线程」绝对是初级面试里的高频考点——几乎所有校招、1-2年经验的面试都会问到Thread和Runnable有啥区别线程有哪些状态怎么手动创建一个线程很多小白被问懵不是因为知识点难而是分不清“理论”和“实战”要么记混状态流转要么写不出简单的实战代码。今天就用「通俗讲解实战案例面试避坑」的方式把多线程基础吃透看完直接能应对面试也能上手写demo文末附面试高频提问总结建议收藏备用✨一、先搞懂为什么需要多线程在讲Thread和Runnable之前先想一个简单的问题我们为什么要用多线程举个生活中的例子你煮泡面的时候不需要一直盯着锅——可以同时烧开水、拿调料包、洗杯子几件事并行做节省总时间。Java程序也是一样单线程程序是“排队做事”一次只能执行一个任务效率很低而多线程能让多个任务“并行执行”比如同时处理多个用户请求、异步处理日志提升程序的执行效率和响应速度。这也是后端开发中多线程的核心作用提升程序并发能力优化执行效率。二、核心知识点Thread vs Runnable面试必问创建Java线程最常用的两种方式就是「继承Thread类」和「实现Runnable接口」。两者的区别是初级面试的重中之重记住一句话Runnable是任务Thread是线程载体。1.方式一继承Thread类Thread类是Java提供的「线程类」继承它之后重写run()方法就能定义线程要执行的任务调用start()方法就能启动线程。实战代码直接复制可运行// 1. 继承Thread类 class MyThread extends Thread { // 重写run()方法定义线程要执行的任务 Override public void run() { for (int i 0; i 5; i) { // Thread.currentThread().getName()获取当前线程名称 System.out.println(Thread.currentThread().getName() 执行任务 i); try { // 线程休眠100毫秒模拟任务执行耗时 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } // 测试类 public class ThreadDemo { public static void main(String[] args) { // 2. 创建线程对象 MyThread thread1 new MyThread(); MyThread thread2 new MyThread(); // 3. 给线程设置名称便于区分 thread1.setName(线程A); thread2.setName(线程B); // 4. 启动线程注意不能调用run()方法否则就是单线程 thread1.start(); thread2.start(); } }运行结果重点观察线程A和线程B会交替执行不会一直是A执行完再执行B——这就是多线程的“并行”效果实际是CPU快速切换看起来并行。⚠️面试避坑启动线程必须用start()方法不能用run()调用run()方法本质还是主线程在执行不会开启新线程。2.方式二实现Runnable接口Runnable接口是Java提供的「任务接口」里面只有一个抽象方法run()用来定义线程要执行的任务。它的核心优势解决Java单继承的局限性一个类继承了Thread就不能继承其他类但可以实现多个接口。实战代码直接复制可运行// 1. 实现Runnable接口定义任务 class MyRunnable implements Runnable { Override public void run() { for (int i 0; i 5; i) { System.out.println(Thread.currentThread().getName() 执行任务 i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } // 测试类 public class RunnableDemo { public static void main(String[] args) { // 2. 创建任务对象不是线程对象 MyRunnable task new MyRunnable(); // 3. 创建线程对象把任务传入Thread线程载体承载任务 Thread thread1 new Thread(task, 线程C); Thread thread2 new Thread(task, 线程D); // 4. 启动线程 thread1.start(); thread2.start(); } }运行结果和继承Thread类类似线程C和线程D交替执行。3. Thread和Runnable核心区别面试必背用表格总结清晰好记面试直接说对比维度Thread类Runnable接口本质线程类本身承载线程和任务任务接口只定义任务不承载线程继承限制单继承不能继承其他类无继承限制可实现多个接口资源共享难实现需静态变量易实现多个线程共用一个任务对象推荐度低有继承限制高灵活、无限制补充实际开发中我们几乎不用继承Thread类都是用Runnable接口或Lambda表达式简化下文实战会讲因为它更灵活也符合“单一职责”设计原则任务和线程分离。三、核心考点线程的5种状态图文流转记牢不混线程从创建到销毁会经历5种状态这是面试高频提问比如线程有哪些状态sleep和wait会让线程进入什么状态结合图文理解不用死记硬背。1.线程的5种状态Java官方定义用通俗的语言解释结合生活场景一看就懂•新建状态New创建了线程对象但还没调用start()方法比如new Thread()后线程就是新建状态相当于“泡面刚放进碗里还没加水”。•就绪状态Runnable调用了start()方法后线程等待CPU调度相当于“泡面对好了等你拿起叉子吃”CPU就是“你”调度就是“拿起叉子”。•运行状态RunningCPU调度到该线程线程执行run()方法里的任务相当于“你正在吃泡面”。•阻塞状态Blocked线程暂时停止执行等待某个条件满足后再回到就绪状态相当于“吃泡面中途接了个电话暂停吃面挂了电话再继续吃”。•死亡状态Terminated线程执行完run()方法或被中断、异常终止相当于“泡面吃完了碗空了”线程彻底结束不能再启动。2.线程状态流转图面试画出来加分核心流转路径记准不要乱新建状态New→就绪状态Runnable【调用start()】→运行状态Running【CPU调度】→阻塞状态Blocked【sleep/wait/同步锁】→就绪状态Runnable【阻塞条件解除】→运行状态Running→死亡状态Terminated【任务执行完/异常】⚠️重点提醒•线程一旦进入死亡状态就不能再调用start()方法重启相当于泡面吃完了不能再泡一次。•sleep()和wait()的区别面试常问sleep()会让线程进入阻塞状态但不会释放锁wait()会让线程进入阻塞状态且会释放锁后续会专门讲先记核心。四、实战升级用Lambda简化Runnable模拟真实场景初级面试中除了让你写Thread和Runnable的demo还可能让你“模拟多线程处理任务”比如多线程下载文件、多线程统计数据。下面用Lambda表达式简化RunnableJava 8特性面试写出来更加分模拟「多线程处理用户请求」的场景实战代码可直接运行、直接套用。实战场景3个线程同时处理3个用户请求public class LambdaThreadDemo { public static void main(String[] args) { // 用Lambda表达式简化Runnable不用单独写实现类 // 线程1处理用户A的请求 Thread threadA new Thread(() - { System.out.println(用户A请求处理中...); try { // 模拟处理耗时200毫秒 Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(用户A请求处理完成✅); }, 处理线程1); // 线程2处理用户B的请求 Thread threadB new Thread(() - { System.out.println(用户B请求处理中...); try { Thread.sleep(150); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(用户B请求处理完成✅); }, 处理线程2); // 线程3处理用户C的请求 Thread threadC new Thread(() - { System.out.println(用户C请求处理中...); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(用户C请求处理完成✅); }, 处理线程3); // 启动所有线程 threadA.start(); threadB.start(); threadC.start(); } }运行结果分析三个线程会同时启动处理请求的顺序不确定因为CPU调度随机但最终都会执行完任务——这就是真实后端开发中多线程处理并发请求的简单模拟。面试加分点能说出“多线程处理请求能提升系统响应速度避免单个请求耗时过长导致其他请求阻塞”。五、面试高频提问总结背会直接应对面试整理了初级面试中多线程基础的5个高频问题答案直接记不用再翻书1.QThread和Runnable的区别A① Thread是线程类承载线程和任务Runnable是任务接口只定义任务。② Thread有单继承限制Runnable无限制。③推荐用Runnable更灵活、易实现资源共享。2.Q启动线程用start()还是run()为什么A用start()因为start()会开启新线程让线程进入就绪状态等待CPU调度而run()只是主线程调用的普通方法不会开启新线程。3.QJava线程有哪些状态A新建New、就绪Runnable、运行Running、阻塞Blocked、死亡Terminated共5种。4.Q线程进入阻塞状态的常见场景A①调用sleep()方法②调用wait()方法③等待同步锁synchronized④等待IO操作比如文件读取。5.Q线程死亡后还能重启吗A不能线程进入死亡状态任务执行完/异常终止就不能再调用start()方法重启只能重新创建线程对象。最后总结多线程基础是Java后端面试的“敲门砖”——初级面试不会问太难的源码重点考察你对「Thread/Runnable区别」「线程状态」「实战代码」的掌握。今天的内容从基础讲解到实战案例再到面试总结覆盖了所有初级考点建议大家把实战代码复制到IDE里运行一遍亲手感受多线程的执行效果比死记硬背更有效。收藏本文面试前快速过一遍轻松应对多线程基础提问评论区留言你面试时被多线程问过哪些问题一起交流避坑