首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

面试官:线程池遇到未处理的异常会崩溃吗?

编程知识
2024年09月13日 14:21

首先,这个问题考察的是你对线程池 execute 方法和 submit 方法的理解,在 Java 线程池的使用中,我们可以通过 execute 方法或 submit 方法给线程池添加任务,但如果线程池中的程序在执行时,遇到了未处理的异常会怎么呢?接下来我们一起来看。

1.execute方法

execute 方法用于提交一个不需要返回值的任务给线程池执行,它接收一个 Runnable 类型的参数,并且不返回任何结果。

它的使用示例代码如下:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecuteDemo {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        
        // 使用 execute 方法提交任务
        executor.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("Task running in " + Thread.currentThread().getName());
                try {
                    // 模拟任务执行
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    System.err.println("Task was interrupted");
                }
                System.out.println("Task finished");
            }
        });

        // 关闭线程池
        executor.shutdown();
    }
}

2.submit方法

submit 方法用于提交一个需要返回值的任务(Callable 对象),或者不需要返回值但希望获取任务状态的任务(Runnable 对象,但会返回一个 Future 对象)。

它接收一个 Callable 或 Runnable 类型的参数,并返回一个 Future 对象,通过该对象可以获取任务的执行结果或检查任务的状态。

2.1 提交Callable任务

示例代码如下:

import java.util.concurrent.Callable;  
import java.util.concurrent.ExecutionException;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
import java.util.concurrent.Future;  
  
public class SubmitCallableDemo {  
    public static void main(String[] args) {  
        // 创建一个固定大小的线程池  
        ExecutorService executorService = Executors.newFixedThreadPool(2);  
  
        // 提交一个 Callable 任务给线程池执行  
        Future<String> future = executorService.submit(new Callable<String>() {  
            @Override  
            public String call() throws Exception {  
                Thread.sleep(2000); // 模拟任务执行时间  
                return "Task's execution result";  
            }  
        });  
  
        try {  
            // 获取任务的执行结果  
            String result = future.get();  
            System.out.println("Task result: " + result);  
        } catch (InterruptedException | ExecutionException e) {  
            e.printStackTrace();  
        }  
  
        // 关闭线程池  
        executorService.shutdown();  
    }  
}

2.2 提交Runnable任务

提交 Runnable 任务并获取 Future 对象,示例代码如下:

import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
import java.util.concurrent.Future;  
  
public class SubmitRunnableDemo {  
    public static void main(String[] args) {  
        // 创建一个固定大小的线程池  
        ExecutorService executorService = Executors.newFixedThreadPool(2);  
  
        // 提交一个 Runnable 任务给线程池执行,并获取一个 Future 对象  
        Future<?> future = executorService.submit(new Runnable() {  
            @Override  
            public void run() {  
                System.out.println("Task is running in thread: " + Thread.currentThread().getName());  
            }  
        });  
  
        // 检查任务是否完成(这里只是为了示例,实际使用中可能不需要这样做)  
        if (future.isDone()) {  
            System.out.println("Task is done");  
        } else {  
            System.out.println("Task is not done yet");  
        }  
  
        // 关闭线程池  
        executorService.shutdown();  
    }  
}

3.遇到未处理异常

线程池遇到未处理的异常执行行为和添加任务的方法有关,也就是说 execute 方法和 submit 方法在遇到未处理的异常时执行行为是不一样的

3.1 execute方法遇到未处理异常

示例代码如下:

import java.util.concurrent.*;

public class ThreadPoolExecutorExceptionTest {
    public static void main(String[] args)  {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                1,
                1,
                1000,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<Runnable>(100));
        // 添加任务一
        executor.execute(() -> {
            String tName = Thread.currentThread().getName();
            System.out.println("线程名:" + tName);
            throw new RuntimeException("抛出异常");
        });
        // 添加任务二
        executor.execute(() -> {
            String tName = Thread.currentThread().getName();
            System.out.println("线程名:" + tName);
            throw new RuntimeException("抛出异常");
        });
    }
}

以上程序的执行结果如下:

从上述结果可以看出,线程池中的核心和最大线程数都为 1 的情况下,到遇到未处理的异常时,执行任务的线程却不一样,这说明了:当使用 execute 方法时,如果遇到未处理的异常,会抛出未捕获的异常,并将当前线程进行销毁

3.2 submit方法遇到未处理异常

然而,当我们将线程池的添加任务方法换成 submit() 之后,执行结果又完全不同了,以下是示例代码:

import java.util.concurrent.*;

public class ThreadPoolExecutorExceptionTest {
    public static void main(String[] args)  {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                1,
                1,
                1000,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<Runnable>(100));
        // 添加任务一
       Future<?> future = executor.submit(() -> {
            String tName = Thread.currentThread().getName();
            System.out.println("线程名:" + tName);
            throw new RuntimeException("抛出异常");
        });
        // 添加任务二
        Future<?> future2 =executor.submit(() -> {
            String tName = Thread.currentThread().getName();
            System.out.println("线程名:" + tName);
            throw new RuntimeException("抛出异常");
        });
        try {
            future.get();
        } catch (Exception e) {
            System.out.println("遇到异常:"+e.getMessage());
        }
        try {
            future2.get();
        } catch (Exception e) {
            System.out.println("遇到异常:"+e.getMessage());
        }
    }
}

以上程序的执行结果如下:

从上述结果可以看出,submit 方法遇到未处理的异常时,并将该异常封装在 Future 的 get 方法中,而不会直接影响执行任务的线程,这样线程就可以继续复用了

小结

线程池在遇到未处理的异常时,不同添加任务的方法的执行行为是不同的:

  • execute 方法:遇到未处理的异常,线程会崩溃,并打印异常信息。
  • submit 方法:遇到未处理的异常,线程本身不会受到影响(线程可以复用),只是将异常信息封装到返回的对象 Future 中。

课后思考

为什么遇到未处理的异常时,execute 方法中的线程会崩溃,而 submit 方法中的线程却可以复用?

本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。

From:https://www.cnblogs.com/vipstone/p/18412245
本文地址: http://shuzixingkong.net/article/1981
0评论
提交 加载更多评论
其他文章 支付宝携手HarmonyOS SDK打造高效便捷的扫码支付体验
背景 在日常的购物转账、生活缴费等在线支付中,用户在正式拉起支付界面前,均需要至少经历一次&quot;识别&quot;+两次&quot;寻找&quot;,即识别归属应用、寻找应用、寻找扫码入口,才能完成扫码、付款,每一步都带来不同程度的用户流失。如何将步骤繁琐的扫码支付做到最简化,是优化在线支付体验
支付宝携手HarmonyOS SDK打造高效便捷的扫码支付体验 支付宝携手HarmonyOS SDK打造高效便捷的扫码支付体验 支付宝携手HarmonyOS SDK打造高效便捷的扫码支付体验
常回家看看之house_of_emma
house_of_emma 前言: 相比较于house_of_kiwi(house_of_kiwi),house_of_emma的手法更加***钻,而且威力更大,条件比较宽松,只需要lagebin_attack即可完成。 当然把两着放到一起是因为它们都利用了__malloc_assest来刷新IO流
常回家看看之house_of_emma 常回家看看之house_of_emma 常回家看看之house_of_emma
《熬夜整理》保姆级系列教程-玩转Wireshark抓包神器教程(6)-Wireshark抓包界面详解
1.简介 在此之前,宏哥已经介绍和讲解过Wireshark的启动界面。但是很多初学者还会碰到一个难题,就是感觉wireshark抓包界面上也是同样的问题很多东西不懂怎么看。其实还是挺明了的宏哥今天就单独写一篇对其抓包界面进行详细地介绍和讲解一下。 2.Wireshak抓包界面概览 通过上一篇我们知道
《熬夜整理》保姆级系列教程-玩转Wireshark抓包神器教程(6)-Wireshark抓包界面详解 《熬夜整理》保姆级系列教程-玩转Wireshark抓包神器教程(6)-Wireshark抓包界面详解 《熬夜整理》保姆级系列教程-玩转Wireshark抓包神器教程(6)-Wireshark抓包界面详解
CPP在内网穿透技术的思考
概述 内网穿透是一种技术,用于在私有局域网(LAN)中的设备与外部网络(如互联网)之间建立通信通道,使得外部设备可以访问内网中的服务。由于内网设备通常位于防火墙或 NAT(网络地址转换)设备之后,外部网络无法直接访问它们。因此,内网穿透技术旨在解决这一问题。本文将讨论如何使用 C++ 实现内网穿透技
经典前端+后端+表头+表身的开发实战参考简易模板【珍藏】
前端部分(Vue 3 + Element Plus) 1. 修改 MPS002HList.vue(主生产计划列表) a. 添加查询表单 在模板中添加查询表单,包含产品料号、品名、规格和年月的输入项。 &lt;template&gt; &lt;div&gt; &lt;!-- 查询表单 --&gt; &
二分图最大权完美匹配
问题 给定一个二分图,左部有 \(n\) 个点,右部有 \(m\) 个点,边 \((u_i, v_j)\) 的边权为 \(A_{i,j}\)。求该二分图的最大权完美匹配。 转化 问题可以写成线性规划的形式,设 \(f_{i, j}\) 表示匹配中是否有边 \((u_i, v_j)\),求 \[\be
Linux下Shell脚本实现统一管理服务启停重启
公司今年开始了大批量的裁员,人心惶惶,所以强迫自己学习点新知识,刚好领导给找了个事情,让写个脚本实现一键启停Linux服务器上的服务,于是开始研究这个怎么搞。 最开始的时候,有点想当然了,觉得一键启停不就是写个菜单,调用一下服务启动停止的命令就可以实现,但是在写的过程中,发现全是坑,搞的心态都崩了,
架构师备考的一些思考(四)
前言 对于数学,我们之前学的是对的,但不是真的,所以我们没有数学思维。 对于计算机,我们学校教的是对的,但不是真的,所以仅仅从学校学习知识的应届毕业生,不论985,211,本科,专科都一样,都是一张白纸,啥也不会。 案例分析 案例分析是5选3,第一题必答。 问题一的类型 架构风格对比 问题二的类型
架构师备考的一些思考(四)