面渣逆袭并发编程篇(暗黑版)V2.1
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
No. 1 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
前⾔
4 万字 145 张⼿绘图,详解 71 道 Java 多线程⾯试⾼频题(让天下没有难背的⼋股),⾯渣
背会这些并发编程⼋股⽂,这次吊打⾯试官,我觉得稳了(⼿动 dog)。
第⼀版作者是⼆哥编程星球的嘉宾三分恶,第⼆版由⼆哥结合球友们的⾯经+技术派
+PmHub+mydb 的项⽬进⾏全新升级。更适合拿来背诵突击⾯试+底层原理理解。
亮⽩版本更适合拿出来打印,这也是很多学⽣党喜欢的⽅式,打印出来背诵的效率会更⾼。
2025 年 01 ⽉ 22 ⽇开始着⼿第⼆版更新。
对于⾼频题,会标注在《Java ⾯试指南(付费)》中出现的位置,哪家公司,原题是什
么,并且会加 ,⽬录⼀⽬了然;如果你想节省时间的话,可以优先背诵这些题⽬,尽
快做到知彼知⼰,百战不殆。
No. 2 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
区分⼋股精华回答版本和原理底层解释,让⼤家知其然知其所以然,同时⼜能做到⾯试时
的⾼效回答。
结合项⽬(技术派、pmhub)来组织语⾔,让⾯试官最⼤程度感受到你的诚意,⽽不是
机械化的背诵。
修复第⼀版中出现的问题,包括球友们的私信反馈,⽹站留⾔区的评论,以及 GitHub 仓
库中的 issue,让这份⾯试指南更加完善。
增加⼆哥编程星球的球友们拿到的⼀些 offer,对⾯渣逆袭的感谢,以及对简历修改的⼀
些认可,以此来激励⼤家,给⼤家更多信⼼。
优化排版,增加⼿绘图,重新组织答案,使其更加⼝语化,从⽽更贴近⾯试官的预期。
⻓按识别下⾯的⼆维码,关注⼆哥的公众号,回复【222】即可拉取最新版本。
No. 3 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
当然了,请允许我的⼀点点私⼼,那就是星球的 PDF 版本会⽐公众号早⼀个⽉时间,毕竟星
球⽤户都付费过了,我有必要让他们先享受到⼀点点福利。相信⼤家也都能理解,毕竟在线
版是免费的,CDN、服务器、域名、OSS 等等都是需要成本的。
更别说我付出的时间和精⼒了,⼤家觉得有帮助还请给个⼝碑,让你身边的同事、同学都能
受益到。
No. 4 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
我把⼆哥的 Java 进阶之路、JVM 进阶之路、并发编程进阶之路,以及所有⾯渣逆袭的版本都
放进来了,涵盖 Java基础、Java集合、Java并发、JVM、Spring、MyBatis、计算机⽹络、
操作系统、MySQL、Redis、RocketMQ、分布式、微服务、设计模式、Linux 等 16 个⼤的
主题,共有 40 多万字,2000+张⼿绘图,可以说是诚意满满。
展示⼀下暗⿊版本的 PDF 吧,排版清晰,字体优雅,更加适合夜服,晚上看会更舒服⼀点。
No. 5 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
基础
1.并⾏跟并发有什么区别?
并⾏是多核 CPU 上的多任务处理,多个任务在同⼀时间真正地同时执⾏。
并发是单核 CPU 上的多任务处理,多个任务在同⼀时间段内交替执⾏,通过时间⽚轮转
实现交替执⾏,⽤于解决 IO 密集型任务的瓶颈。
No. 6 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
举个例⼦,就好像我们去⻝堂打饭,并⾏就是每个⼈对应⼀个阿姨,同时打饭;⽽并发就是
⼀个阿姨,轮流给每个⼈打饭,假如有个⼈磨磨唧唧,阿姨就会吆喝下⼀个⼈,这样就能提
⾼⻝堂的打饭效率。
你是如何理解线程安全的?
推荐阅读:多线程带来了哪些问题?
如果⼀段代码块或者⼀个⽅法被多个线程同时执⾏,还能够正确地处理共享数据,那么这段
代码块或者这个⽅法就是线程安全的。
No. 7 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
可以从三个要素来确保线程安全:
①、原⼦性:⼀个操作要么完全执⾏,要么完全不执⾏,不会出现中间状态。
可以通过同步关键字 synchronized 或原⼦操作,如 AtomicInteger 来保证原⼦性。
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet(); // 原⼦操作
②、可⻅性:当⼀个线程修改了共享变量,其他线程能够⽴即看到变化。
No. 8 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
可以通过 volatile 关键字来保证可⻅性。
private volatile String itwanger = "沉默王⼆";
③、有序性:要确保线程不会因为死锁、饥饿、活锁等问题导致⽆法继续执⾏。
No. 9 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
1. Java ⾯试指南(付费)收录的华为 OD ⾯经同学 1 ⼀⾯⾯试原题:对于多线程
编程的了解?
2. Java ⾯试指南(付费)收录的快⼿⾯经同学 1 部⻔主站技术部⾯试原题:你对
线程安全的理解是什么?
memo:2025 年 1 ⽉ 22 ⽇修改⾄此。
2.说说进程和线程的区别?
推荐阅读:进程与线程的区别是什么?
进程说简单点就是我们在电脑上启动的⼀个个应⽤。它是操作系统分配资源的最⼩单位。
线程是进程中的独⽴执⾏单元。多个线程可以共享同⼀个进程的资源,如内存;每个线程都
有⾃⼰独⽴的栈和寄存器。
No. 10 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
如何理解协程?
协程被视为⽐线程更轻量级的并发单元,可以在单线程中实现并发执⾏,由我们开发者显式
调度。
协程是在⽤户态进⾏调度的,避免了线程切换时的内核态开销。
Java ⾃身是不⽀持携程的,我们可以使⽤ Quasar、Kotlin 等框架来实现协程。
fun main() = runBlocking {
launch {
delay(1000L)
println("World!")
}
println("Hello,")
}
线程间是如何进⾏通信的?
原则上可以通过消息传递和共享内存两种⽅法来实现。Java 采⽤的是共享内存的并发模型。
No. 11 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
这个模型被称为 Java 内存模型,简写为 JMM,它决定了⼀个线程对共享变量的写⼊,何时
对另外⼀个线程可⻅。当然了,本地内存是 JMM 的⼀个抽象概念,并不真实存在。
⽤⼀句话来概括就是:共享变量存储在主内存中,每个线程的私有本地内存,存储的是这个
共享变量的副本。
线程 A 与线程 B 之间如要通信,需要要经历 2 个步骤:
线程 A 把本地内存 A 中的共享变量副本刷新到主内存中。
线程 B 到主内存中读取线程 A 刷新过的共享变量,再同步到⾃⼰的共享变量副本中。
No. 12 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
1. Java ⾯试指南(付费)收录的字节跳动商业化⼀⾯的原题:进程和线程区别,
线程共享内存和进程共享内存的区别
2. Java ⾯试指南(付费)收录的⼩⽶春招同学 K ⼀⾯⾯试原题:协程和线程和进
程的区别
3. Java ⾯试指南(付费)收录的字节跳动⾯经同学 1 Java 后端技术⼀⾯⾯试原
题:线程和进程有什么区别?
4. Java ⾯试指南(付费)收录的华为 OD ⾯经同学 1 ⼀⾯⾯试原题:对于多线程
编程的了解?
5. Java ⾯试指南(付费)收录的美团⾯经同学 2 Java 后端技术⼀⾯⾯试原题:进
程和线程的区别?
6. Java ⾯试指南(付费)收录的华为⾯经同学 9 Java 通⽤软件开发⼀⾯⾯试原
题:进程和线程的区别
7. Java ⾯试指南(付费)收录的 ⼩公司⾯经合集好未来测开⾯经同学 3 测开⼀⾯
⾯试原题:进程和线程的区别
8. Java ⾯试指南(付费)收录的招商银⾏⾯经同学 6 招银⽹络科技⾯试原题:进
程和线程的区别?
9. Java ⾯试指南(付费)收录的⽤友⾯试原题:线程和进程的区别
10. Java ⾯试指南(付费)收录的vivo ⾯经同学 10 技术⼀⾯⾯试原题:线程的概
念,线程有哪些状态
No. 13 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
11. Java ⾯试指南(付费)收录的海康威视同学 4⾯试原题:对协程的了解,为什
么协程⽐线程还有更低的资源消耗
memo:2025 年 1 ⽉ 23 ⽇修改⾄此。
3.说说线程有⼏种创建⽅式?
推荐阅读:室友打了⼀把王者就学会了 Java 多线程
有三种,分别是继承 Thread 类、实现 Runnable 接⼝、实现 Callable 接⼝。
第⼀种需要重写⽗类 Thread 的 run() ⽅法,并且调⽤ start() ⽅法启动线程。
class ThreadTask extends Thread {
public void run() {
System.out.println("看完⼆哥的 Java 进阶之路,上岸了!");
}
public static void main(String[] args) {
ThreadTask task = new ThreadTask();
task.start();
}
}
这种⽅法的缺点是,如果 ThreadTask 已经继承了另外⼀个类,就不能再继承 Thread 类了,
因为 Java 不⽀持多重继承。
No. 14 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
第⼆种需要重写 Runnable 接⼝的 run() ⽅法,并将实现类的对象作为参数传递给 Thread
对象的构造⽅法,最后调⽤ start() ⽅法启动线程。
class RunnableTask implements Runnable {
public void run() {
System.out.println("看完⼆哥的 Java 进阶之路,上岸了!");
}
public static void main(String[] args) {
RunnableTask task = new RunnableTask();
Thread thread = new Thread(task);
thread.start();
}
}
这种⽅法的优点是可以避免 Java 的单继承限制,并且更符合⾯向对象的编程思想,因为
Runnable 接⼝将任务代码和线程控制的代码解耦了。
第三种需要重写 Callable 接⼝的 call() ⽅法,然后创建 FutureTask 对象,参数为 Callable
实现类的对象;紧接着创建 Thread 对象,参数为 FutureTask 对象,最后调⽤ start() ⽅
法启动线程。
No. 15 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
class CallableTask implements Callable<String> {
public String call() {
return "看完⼆哥的 Java 进阶之路,上岸了!";
}
public static void main(String[] args) throws ExecutionException,
InterruptedException {
CallableTask task = new CallableTask();
FutureTask<String> futureTask = new FutureTask<>(task);
Thread thread = new Thread(futureTask);
thread.start();
System.out.println(futureTask.get());
}
}
这种⽅法的优点是可以获取线程的执⾏结果。
⼀个 8G 内存的系统最多能创建多少个线程?
推荐阅读:深⼊理解 JVM 的运⾏时数据区
理论上⼤约 8000 个。
创建线程的时候,⾄少需要分配⼀个虚拟机栈,在 64 位操作系统中,默认⼤⼩为 1M,因此
⼀个线程⼤约需要 1M 的内存。
但 JVM、操作系统本身的运⾏就要占⼀定的内存空间,所以实际上可以创建的线程数远⽐
8000 少。
详细解释⼀下。
可以通过 java -XX:+PrintFlagsFinal -version | grep ThreadStackSize 命令查看
JVM 栈的默认⼤⼩。
No. 16 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
其中 ThreadStackSize 的单位是 KB,也就是说默认的 JVM 栈⼤⼩是 1024 KB,也就是
1M。
启动⼀个 Java 程序,你能说说⾥⾯有哪些线程吗?
⾸先是 main 线程,这是程序执⾏的⼊⼝。
然后是垃圾回收线程,它是⼀个后台线程,负责回收不再使⽤的对象。
还有编译器线程,⽐如 JIT,负责把⼀部分热点代码编译后放到 codeCache 中。
No. 17 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
可以通过下⾯的代码进⾏检测:
No. 18 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
class ThreadLister {
public static void main(String[] args) {
// 获取所有线程的堆栈跟踪
Map<Thread, StackTraceElement[]> threads =
Thread.getAllStackTraces();
for (Thread thread : threads.keySet()) {
System.out.println("Thread: " + thread.getName() + " (ID="
+ thread.getId() + ")");
}
}
}
结果如下所示:
Thread: Monitor Ctrl-Break (ID=5)
Thread: Reference Handler (ID=2)
Thread: main (ID=1)
Thread: Signal Dispatcher (ID=4)
Thread: Finalizer (ID=3)
简单解释下:
Thread: main (ID=1) - 主线程,Java 程序启动时由 JVM 创建。
Thread: Reference Handler (ID=2) - 这个线程是⽤来处理引⽤对象的,如软引⽤、
弱引⽤和虚引⽤。负责清理被 JVM 回收的对象。
Thread: Finalizer (ID=3) - 终结器线程,负责调⽤对象的 finalize ⽅法。对象在垃圾
回收器标记为可回收之前,由该线程执⾏其 finalize ⽅法,⽤于执⾏特定的资源释放操
作。
Thread: Signal Dispatcher (ID=4) - 信号调度线程,处理来⾃操作系统的信号,将
它们转发给 JVM 进⾏进⼀步处理,例如响应中断、停⽌等信号。
Thread: Monitor Ctrl-Break (ID=5) - 监视器线程,通常由⼀些特定的 IDE 创建,⽤
于在开发过程中监控和管理程序执⾏或者处理中断。
No. 19 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
1. Java ⾯试指南(付费)收录的字节跳动⾯经同学 1 Java 后端技术⼀⾯⾯试原
题:有多少种实现线程的⽅法?
2. Java ⾯试指南(付费)收录的农业银⾏同学 1 ⾯试原题:实现线程的⽅式和区
别
3. Java ⾯试指南(付费)收录的农业银⾏⾯经同学 3 Java 后端⾯试原题:说说线
程的创建⽅法
4. Java ⾯试指南(付费)收录的⼩公司⾯经合集同学 1 Java 后端⾯试原题:线程
创建的⽅式?Runable 和 Callable 有什么区别?
5. Java ⾯试指南(付费)收录的阿⾥⾯经同学 5 阿⾥妈妈 Java 后端技术⼀⾯⾯试
原题:⼀个 8G 内存的系统最多能创建多少线程?(奇怪的问题,答了⼀些
pcb、⻚表、虚拟机栈什么的)启动⼀个 Java 程序,你能说说⾥⾯有哪些线程
吗?
6. Java ⾯试指南(付费)收录的招商银⾏⾯经同学 6 招银⽹络科技⾯试原题:如
何创建线程?
7. Java ⾯试指南(付费)收录的百度⾯经同学 1 ⽂⼼⼀⾔ 25 实习 Java 后端⾯试
原题:java 如何创建线程?每次都要创建新线程来实现异步操作,很繁琐,有了
解线程池吗?
8. Java ⾯试指南(付费)收录的美团⾯经同学 4 ⼀⾯⾯试原题:平时怎么使⽤多
线程
memo:2025 年 1 ⽉ 24 ⽇修改⾄此。
4.调⽤ start ⽅法时会执⾏ run ⽅法,那怎么不直接调⽤ run⽅法?
调⽤ start() 会创建⼀个新的线程,并异步执⾏ run() ⽅法中的代码。
直接调⽤ run() ⽅法只是⼀个普通的同步⽅法调⽤,所有代码都在当前线程中执⾏,不会创
建新线程。没有新的线程创建,也就达不到多线程并发的⽬的。
通过敲代码体验⼀下。
No. 20 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
class MyThread extends Thread {
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start(); // 正确的⽅式,创建⼀个新线程,并在新线程中执⾏ run()
t1.run(); // 仅在主线程中执⾏ run(),没有创建新线程
}
}
来看输出结果:
main
Thread-0
也就是说,调⽤ start() ⽅法会通知 JVM,去调⽤底层的线程调度机制来启动新线程。
No. 21 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
调⽤ start() 后,线程进⼊就绪状态,等待操作系统调度;⼀旦调度执⾏,线程会执⾏其
run() ⽅法中的代码。
1. Java ⾯试指南(付费)收录的⼩公司⾯经合集同学 1 Java 后端⾯试原题:启动
⼀个线程是 run()还是 start()?
2. Java ⾯试指南(付费)收录的百度⾯经同学 1 ⽂⼼⼀⾔ 25 实习 Java 后端⾯试
原题:java 如何启动多线程,有哪些⽅式?
3. ⼆哥编程星球球友枕云眠美团 AI ⾯试原题:java 线程操作中的 start 和 run ⽅法
区别是什么
memo:2025 年 1 ⽉ 26 ⽇修改⾄此。
5.线程有哪些常⽤的调度⽅法?
⽐如说 start ⽅法⽤于启动线程并让操作系统调度执⾏;sleep ⽅法⽤于让当前线程休眠⼀段
时间;wait ⽅法会让当前线程等待,notify 会唤醒⼀个等待的线程。
No. 22 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
说说wait⽅法和notify⽅法?
当线程 A 调⽤共享对象的 wait() ⽅法时,线程 A 会被阻塞挂起,直到:
线程 B 调⽤了共享对象的 notify() ⽅法或者 notifyAll() ⽅法;
其他线程调⽤线程 A 的 interrupt() ⽅法,导致线程 A 抛出 InterruptedException 异
常。
线程 A 调⽤共享对象的 wait(timeout) ⽅法后,没有在指定的 timeout 时间内被其它线程唤
醒,那么这个⽅法会因为超时⽽返回。
当线程 A 调⽤共享对象的 notify() ⽅法后,会唤醒⼀个在这个共享对象上调⽤ wait 系列
⽅法被挂起的线程。
共享对象上可能会有多个线程在等待,具体唤醒哪个线程是随机的。
No. 23 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
如果调⽤的是 notifyAll ⽅法,会唤醒所有在这个共享变量上调⽤ wait 系列⽅法⽽被挂起的线
程。
说说 sleep ⽅法?
当线程 A 调⽤了 Thread 的 sleep ⽅法后,线程 A 会暂时让出指定时间的执⾏权。
指定的睡眠时间到了后该⽅法会正常返回,接着参与 CPU 调度,获取到 CPU 资源后可以继
续执⾏。
说说yield⽅法?
yield() ⽅法的⽬的是让当前线程让出 CPU 使⽤权,回到就绪状态。但是线程调度器可能
会忽略。
说说interrupt⽅法?
推荐阅读:interrupt ⽅法
interrupt() ⽅法⽤于通知线程停⽌,但不会直接终⽌线程,需要线程⾃⾏处理中断标志。
常与 isInterrupted() 或 Thread.interrupted() 配合使⽤。
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Running");
}
System.out.println("Interrupted");
});
thread.start();
thread.interrupt(); // 中断线程
说说 stop ⽅法?
stop ⽅法⽤来强制停⽌线程,⽬前已经处于废弃状态,因为 stop ⽅法可能会在不⼀致的状态
下释放锁,破坏对象的⼀致性。
No. 24 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
1. Java ⾯试指南(付费)收录的帆软同学 3 Java 后端⼀⾯的原题:怎么停⽌⼀个
线程,interrupt 和 stop 区别
memo:2025 年 1 ⽉ 27 ⽇修改⾄此。
6.线程有⼏种状态?
6 种。
new 代表线程被创建但未启动;runnable 代表线程处于就绪或正在运⾏状态,由操作系统调
度;blocked 代表线程被阻塞,等待获取锁;waiting 代表线程等待其他线程的通知或中断;
timed_waiting 代表线程会等待⼀段时间,超时后⾃动恢复;terminated 代表线程执⾏完毕,
⽣命周期结束。
No. 25 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
也就是说,线程的⽣命周期可以分为五个主要阶段:新建、就绪、运⾏、阻塞和终⽌。线程
在运⾏过程中会根据状态的变化在这些阶段之间切换。
class ThreadStateExample {
public static void main(String[] args) throws InterruptedException
{
Thread thread = new Thread(() -> {
try {
Thread.sleep(2000); // TIMED_WAITING
synchronized (ThreadStateExample.class) {
ThreadStateExample.class.wait(); // WAITING
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
No. 26 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
System.out.println("State after creation: " +
thread.getState()); // NEW
thread.start();
System.out.println("State after start: " + thread.getState());
// RUNNABLE
Thread.sleep(500);
System.out.println("State while sleeping: " +
thread.getState()); // TIMED_WAITING
synchronized (ThreadStateExample.class) {
ThreadStateExample.class.notify(); // 唤醒线程
}
thread.join();
System.out.println("State after termination: " +
thread.getState()); // TERMINATED
}
}
⽤⼀个表格来做个总结:
No. 27 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
状态 说明
NEW 当线程被创建后,如通过 new Thread() ,它处于新建状态。此时,线
程已经被分配了必要的资源,但还没有开始执⾏。
RUNNABLE 当调⽤线程的 start() ⽅法后,线程进⼊可运⾏状态。在这个状态下,
线程可能正在运⾏也可能正在等待获取 CPU 时间⽚,具体取决于线程
调度器的调度策略。
BLOCKED 线程在试图获取⼀个锁以进⼊同步块/⽅法时,如果锁被其他线程持
有,线程将进⼊阻塞状态,直到它获取到锁。
WAITING 线程进⼊等待状态是因为调⽤了如下⽅法之⼀: Object.wait() 或
LockSupport.park() 。在等待状态下,线程需要其他线程显式地唤
醒,否则不会⾃动执⾏。
TIME_WAITING 当线程调⽤带有超时参数的⽅法时,如 Thread.sleep(long
millis) 、 Object.wait(long timeout) 或
LockSupport.parkNanos() ,它将进⼊超时等待状态。线程在指定的
等待时间过后会⾃动返回可运⾏状态。
TERMINATED 当线程的 run() ⽅法执⾏完毕后,或者因为⼀个未捕获的异常终⽌了执
⾏,线程进⼊终⽌状态。⼀旦线程终⽌,它的⽣命周期结束,不能再被
重新启动。
如何强制终⽌线程?
第⼀步,调⽤线程的 interrupt() ⽅法,请求终⽌线程。
第⼆步,在线程的 run() ⽅法中检查中断状态,如果线程被中断,就退出线程。
class MyTask implements Runnable {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
System.out.println("Running...");
Thread.sleep(1000); // 模拟⼯作
} catch (InterruptedException e) {
No. 28 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
// 捕获中断异常后,重置中断状态
Thread.currentThread().interrupt();
System.out.println("Thread interrupted, exiting...");
break;
}
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException
{
Thread thread = new Thread(new MyTask());
thread.start();
Thread.sleep(3000); // 主线程等待3秒
thread.interrupt(); // 请求终⽌线程
}
}
中断结果:
No. 29 / 228
⾯渣逆袭并发编程篇V2-让天下所有的⾯渣都能逆袭
1. Java ⾯试指南(付费)收录的招商银⾏⾯经同学 6 招银⽹络科技⾯试原题:线
程的⽣命周期和状态?
2. Java ⾯试指南(付费)收录的快⼿同学 2 ⼀⾯⾯试原题:线程有哪些状态?
3. Java ⾯试指南(付费)收录的 OPPO ⾯经同学 1 ⾯试原题:Java⾥线程的⽣命
周期
4. Java ⾯试指南(付费)收录的同学 D ⼩⽶⼀⾯原题:线程的⽣命周期
7.什么是线程上下⽂切换?
线程上下⽂切换是指 CPU 从⼀个线程切换到另⼀个线程执⾏时的过程。
在线程切换的过程中,CPU 需要保存当前线程的执⾏状态,并加载下⼀个线程的上下⽂。
之所以要这样,是因为 CPU 在同⼀时刻只能执⾏⼀个线程,为了实现多线程并发执⾏,需要
不断地在多个线程之间切换。
为了让⽤户感觉多个线程是在同时执⾏的, CPU 资源的分配采⽤了时间⽚轮转的⽅式,线程
在时间⽚内占⽤ CPU 执⾏任务。当线程使⽤完时间⽚后,就会让出 CPU 让其他线程占⽤。
No. 30 / 228