关于多线程中抛异常的这个面试题我再说最后一次! (2)

简介: 关于多线程中抛异常的这个面试题我再说最后一次! (2)

两种情况都和 future.get 方法有关,那我们就从这个方法的源码入手。


这个 Future 是一个接口:


image.png


而这个接口有非常多的实现类。我们找哪个实现类呢?


就是下面这个实现类:


java.util.concurrent.FutureTask


至于是怎么找到它的,你慢慢往后看就知道了。


先看看 FutureTask 的 get 方法:


image.png


get 方法的逻辑很简单,首先判断当前状态是否已完成,如果不是,则进入等待,如果是,则进入 report 方法。


一进 get 方法,我们就看到了 state 这个东西,这是 FutureTask 里面一个非常重要的东西:


image.png


所以,一目了然,一个任务的终态有四种:NORMAL、EXCEPTIONAL、CANCELLED、INTERRUPTED。


而我们主要关心 NORMAL、EXCEPTIONAL。


所以再回头看看 get 方法:


image.png


如果当前状态是小于 COMPLEING 的。


也就是当前状态只能是 NEW 或者 COMPLEING,总之就是任务还没有完成。所以进入 awaitDone 方法。这个方法不是本文关心的地方,接着往下看。


程序能往下走,说明当前的状态肯定是下面圈起来的状态中的某一个:


image.png


这个方法是干啥的?


注解说的很清楚了:对于已经完成了的 task,返回其结果或者抛出异常。


这里面的逻辑就很简单了,把 outcome 变量赋值给 x 。


然后判断当前状态,如果是 NORMAL,即 2,说明正常完成,直接返回 x。


如果是大于等于 CANCELLED,即大于等于 4 ,即这几种状态,就抛出 CancellationException。


剩下的情况就抛出 ExecutionException。


image.png


而这个“剩下的情况”是什么情况?


不就只剩下一个 EXCEPTIONAL 的情况了。


所以,经过前面的描述,我们可以总结一下。


当 FutureTask 的 status 为 NORMAL 时正常返回结果,当 status 为 EXCEPTIONAL 时抛出异常。


而当终态为 NORMAL 或者 EXCEPTIONAL 时,按照注释描述,状态的流程只能是这样的:


image.png



那么到底是不是这样的呢?


这就需要我们去线程池里面验证一下了。


寻找答案-线程池


先回答上一节的一个问题:我怎么知道是看 Future 这个接口的 FutureTask 这个实现类的:


image.png


image.png


可以看到,FutureTask 的构造方法里面默认了状态为 NEW。


然后直接在 runWorker 方法的 task.run 方法处打上断点:


image.png


这个 task 是一个 FutureTask,所以 run 方法其实是 FutureTask 的 run 方法。


跟着断点进去之后,就是 FutureTask 的 run 方法:


image.png


答案都藏在这个方法里面。


java.util.concurrent.FutureTask#run


标号为 ① 的地方是执行我们的任务,call 的就是示例代码里面的 sayHi 方法。


如果 sayHi 方法没有捕获运行时异常,则会在标号为 ② 的这个 catch 里面被捕获。然后执行标号为 ② 的这个代码。


如果 sayHi 方法捕获了运行时异常,则会进入标号为 ③ 的这个逻辑里面。


我们分别看一下标号为 ② 和 ③ 的逻辑:


image.png


目录
相关文章
|
2月前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
2月前
|
消息中间件 前端开发 NoSQL
面试官:线程池遇到未处理的异常会崩溃吗?
面试官:线程池遇到未处理的异常会崩溃吗?
74 3
面试官:线程池遇到未处理的异常会崩溃吗?
|
2月前
|
消息中间件 前端开发 NoSQL
面试官:如何实现线程池任务编排?
面试官:如何实现线程池任务编排?
33 1
面试官:如何实现线程池任务编排?
|
3月前
|
Java
【多线程面试题二十五】、说说你对AQS的理解
这篇文章阐述了对Java中的AbstractQueuedSynchronizer(AQS)的理解,AQS是一个用于构建锁和其他同步组件的框架,它通过维护同步状态和FIFO等待队列,以及线程的阻塞与唤醒机制,来实现同步器的高效管理,并且可以通过实现特定的方法来自定义同步组件的行为。
【多线程面试题二十五】、说说你对AQS的理解
|
3月前
|
消息中间件 缓存 算法
Java多线程面试题总结(上)
进程和线程是操作系统管理程序执行的基本单位,二者有明显区别: 1. **定义与基本单位**:进程是资源分配的基本单位,拥有独立的内存空间;线程是调度和执行的基本单位,共享所属进程的资源。 2. **独立性与资源共享**:进程间相互独立,通信需显式机制;线程共享进程资源,通信更直接快捷。 3. **管理与调度**:进程管理复杂,线程管理更灵活。 4. **并发与并行**:进程并发执行,提高资源利用率;线程不仅并发还能并行执行,提升执行效率。 5. **健壮性**:进程更健壮,一个进程崩溃不影响其他进程;线程崩溃可能导致整个进程崩溃。
47 2
|
3月前
|
存储 安全 容器
【多线程面试题二十一】、 分段锁是怎么实现的?
这篇文章解释了分段锁的概念和实现方式,通过将数据分成多个段并在每段数据上使用独立锁,从而降低锁竞争,提高并发访问效率,举例说明了`ConcurrentHashMap`如何使用分段锁技术来实现高并发和线程安全。
【多线程面试题二十一】、 分段锁是怎么实现的?
|
3月前
|
存储 缓存 安全
Java多线程面试题总结(中)
Java内存模型(JMM)定义了程序中所有变量的访问规则与范围,确保多线程环境下的数据一致性。JMM包含主内存与工作内存的概念,通过8种操作管理两者间的交互,确保原子性、可见性和有序性。`synchronized`和`volatile`关键字提供同步机制,前者确保互斥访问,后者保证变量更新的可见性。多线程操作涉及不同状态,如新建(NEW)、可运行(RUNNABLE)等,并可通过中断、等待和通知等机制协调线程活动。`volatile`虽不确保线程安全,但能确保变量更新对所有线程可见。
19 0
|
3月前
|
Java 程序员 容器
【多线程面试题二十四】、 说说你对JUC的了解
这篇文章介绍了Java并发包java.util.concurrent(简称JUC),它是JSR 166规范的实现,提供了并发编程所需的基础组件,包括原子更新类、锁与条件变量、线程池、阻塞队列、并发容器和同步器等多种工具。
|
3月前
|
缓存 Java
【多线程面试题二十三】、 说说你对读写锁的了解volatile关键字有什么用?
这篇文章讨论了Java中的`volatile`关键字,解释了它如何保证变量的可见性和禁止指令重排,以及它不能保证复合操作的原子性。
|
3月前
|
Java
【多线程面试题二十二】、 说说你对读写锁的了解
这篇文章讨论了读写锁(ReadWriteLock)的概念和应用场景,强调了读写锁适用于读操作远多于写操作的情况,并介绍了Java中`ReentrantReadWriteLock`实现的读写锁特性,包括公平性选择、可重入和可降级。