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

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

首先,两个方法都是先进行一个 cas 的操作,把当前 FutureTask 的 status 字段从 NEW 修改为 COMPLETING 。


完成了状态流转的这一步:


image.png


注意这里,如果 cas 操作失败了,则不会进行任何操作。


cas 操作失败了,说明什么呢?


说明当前的状态是 CANCELLED 或者 INTERRUPTING 或者INTERRUPTED。


也就是这个任务被取消了或者被中断了。


那还设置结果干啥,没有任何卵用,对不对。


image.png


如果 cas 操作成功,接着往下看,可以看到虽然入参不一样了,但是都赋给了 outcome 变量,这个变量,在上一节的 report 方法出现过,还记得吗?能不能呼应上?


接下来就是状态接着往下流转。


set 方法表示正常结束,状态流转到 NORMAL。


setException 方法表示任务出现异常,状态流转到 EXCEPTIONAL。



所以经过 FutureTask 的 run 方法后,如果任务没有被中断或者取消,则会通过 setException 或者 set 方法完成状态的流转和 outcome 参数的设置:


image.png


而到底是调用 setException 方法还是 set 方法,取决于标号为 ① 的地方是否会抛出异常。


即取决于任务体是否会抛出异常。


假设 sayHi 方法是这样的,会抛出运行时异常:


image.png


而通过 submit 方法提交任务时写法分别如下:


如果是标号为 ① 的写法,则会进入 setException 方法。


如果是标号为 ② 的写法,则会进入 set 方法。


所以,你现在再回去看看这个题目:


当执行方法是 submit 的时候,如果子线程抛出未经捕获的运行时异常,将会被封装到 Future 里面,那么如果子线程捕获了异常,该异常还会封装到 Future 里面吗?是怎么实现的呢?


现在是不是很清晰了。


如果子线程捕获了异常,该异常不会被封装到 Future 里面。是通过 FutureTask 的 run 方法里面的 setException 和 set 方法实现的。在这两个方法里面完成了 FutureTask 里面的 outcome 变量的设置,同时完成了从 NEW 到 NORMAL 或者 EXCEPTIONAL 状态的流转。


线程池拒绝异常


写文章的时候我突然又想到一个问题。


不论是用 submit 还是 execute 方法往线程池里面提交任务,如果由于线程池满了,导致抛出拒绝异常呢?


RejectedExecutionException 异常也是一个 RuntimeException:


image.png


那么对于这个异常,如果我们不在子线程捕获,是不是也不会打印呢?


假设你不知道这个问题,你就分析一下,从会和不会中猜一个呗。


我猜是会打印的。


因为假设让我来提供一个这样的功能,由于线程池饱和了而拒绝了新任务的提交,我肯定得给使用方一个提示。告诉他有的任务由于线程池满了而没有提交进去。


不然,使用者自己排查到这个问题后,肯定会说一声:这什么傻逼玩意,把异常给吞了?

来,搞个 Demo 验证一下:


image.png


我们定义的这个线程池最大容量是 7 个任务。


在循环体中扔 10 个比较耗时的任务进去。有 3 个任务它处理不了,那么肯定是会触发拒绝策略的。


你觉得这个程序运行后会在控制台打印异常日志吗?会打印几次呢?


看一下运行结果:


image.png


抛出了一次异常,执行完成了 7 个任务。


我们并没有捕获异常,打印堆栈信息的相关代码,那么这个异常是谁打印的?


如果你没有捕获异常,JVM 会帮你调用这个方法:


image.png


image.png


10 个任务,三次异常,完成了 7 个任务。


也不会让 JVM 触发 dispatchUncaughtException 方法了。

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