首页 > 其他分享 >面试官:说说线程池的工作原理?

面试官:说说线程池的工作原理?

时间:2024-03-11 16:44:37浏览次数:14  
标签:面试官 队列 Spring 任务 线程 executor 原理 ThreadPoolExecutor

线程池的底层是基于线程和任务队列来实现的,创建线程池的创建方式通常有以下两种:

  1. 普通 Java 项目,使用 ThreadPoolExecutor 来创建线程池,这点《阿里巴巴Java开发手册》中也有说明,如下图所示:

image.png

  1. Spring 项目中,会使用代码可读性更高的 ThreadPoolTaskExecutor 来创建线程池,虽然它的底层也是通过 ThreadPoolExecutor 来实现的,但 ThreadPoolTaskExecutor 可读性更高,因为它不需要在构造方法中设置参数,而是通过属性设置的方式来设置参数的,所以可读性更高。

Spring 内置的线程池 ThreadPoolTaskExecutor 的使用示例如下:

@Configuration
public class AsyncConfig {
    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程数
        executor.setCorePoolSize(5);
        // 最大线程数
        executor.setMaxPoolSize(10);
        // 队列容量
        executor.setQueueCapacity(20);
        // 线程池维护线程所允许的空闲时间
        executor.setKeepAliveSeconds(60);
        // 线程池对拒绝任务(无线程可用)的处理策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 初始化
        executor.initialize();
        return executor;
    }
}

1.线程池工作流程

当有任务来了之后,线程池的执行流程是这样的:

  1. 先判断当前线程数是否大于核心线程数,如果结果为 false,则新建线程并执行任务。
  2. 如果大于核心线程数,则判断任务队列是否已满,如果结果为 false,则把任务添加到任务队列中等待线程执行。
  3. 如果任务队列已满,则判断当前线程数量是否超过最大线程数,如果结果为 false,则新建线程执行此任务。
  4. 如果超过最大线程数,则将执行线程池的拒绝策略。

如下图所示:

2.拒绝策略

当线程池无法接受新任务时,会触发拒绝策略,内置的拒绝策略有四种:

  1. AbortPolicy:默认策略,直接抛出 RejectedExecutionException 异常。
  2. CallerRunsPolicy:由调用者线程执行任务。
  3. DiscardPolicy:默默地丢弃任务,没有任何异常抛出。
  4. DiscardOldestPolicy:尝试抛弃队列中最旧的任务,然后重新尝试提交当前任务。

除了内置的拒绝策略之外,我们还可以设置自定义拒绝策略,它的实现如下:

import java.util.concurrent.RejectedExecutionHandler;  
import java.util.concurrent.ThreadPoolExecutor;  
  
public class CustomRejectedExecutionHandler implements RejectedExecutionHandler {  
  
    @Override  
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {  
        // 在这里处理拒绝的任务  
        System.err.println("任务被拒绝执行: " + r.toString());  
        // 可以选择记录日志、抛出自定义异常或采取其他措施  
        // 例如,可以将任务保存到某个队列中,稍后再尝试重新执行  
    }  
}

使用自定义拒绝策略:

import java.util.concurrent.ArrayBlockingQueue;  
import java.util.concurrent.ThreadPoolExecutor;  
import java.util.concurrent.TimeUnit;  
  
public class ThreadPoolDemo {  
  
    public static void main(String[] args) {  
        // 配置线程池参数  
        int corePoolSize = 5;  
        int maximumPoolSize = 10;  
        long keepAliveTime = 60L;  
        TimeUnit unit = TimeUnit.SECONDS;  
        int queueCapacity = 25;  
  
        // 创建一个阻塞队列  
        ArrayBlockingQueue<Runnable> workQueue = 
            new ArrayBlockingQueue<>(queueCapacity);  
  
        // 创建 ThreadPoolExecutor 实例  
        ThreadPoolExecutor executor = new ThreadPoolExecutor(  
                corePoolSize,  
                maximumPoolSize,  
                keepAliveTime,  
                unit,  
                workQueue,  
                new CustomRejectedExecutionHandler() // 使用自定义的拒绝策略  
        );  
  
        // 提交任务  
        for (int i = 0; i < 50; i++) {  
            final int taskId = i;  
            executor.execute(() -> {  
                System.out.println("执行任务: " + taskId + " 由线程 " + Thread.currentThread().getName() + " 执行");  
                try {  
                    Thread.sleep(1000); // 模拟耗时任务  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            });  
        }  
  
        // 关闭线程池(这不会立即停止所有正在执行的任务)  
        executor.shutdown();  
    }  
}

课后反思

实际项目中线程池会使用哪种拒绝策略?为什么?线程池是通过什么机制来创建线程的?线程池创建线程时可以设置哪些属性?

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

标签:面试官,队列,Spring,任务,线程,executor,原理,ThreadPoolExecutor
From: https://www.cnblogs.com/vipstone/p/18066490

相关文章

  • Java多线程基础用法
    线程创建线程创建的三种方式:Thread(继承Thread类)自定义线程类继承Thread类重写run()方法。编写线程执行体创建线程对象,调用start()方法启动线程packagecom.lily.demo01;publicclassTestThreadextendsThread{@Overridepublicvoidrun(){for......
  • SpringBoot自动配置原理解析
    一、什么是SpringBoot自动配置首先介绍一下什么是SpringBoot,SpringBoost是基于Spring框架开发出来的功能更强大的Java程序开发框架,其最主要的特点是:能使程序开发者快速搭建一套开发环境。SpringBoot能将主流的开发框架(例如SpringMVC,Dubbo,Mybatis,Redis等),做到像Maven导......
  • QT 多线程
     第一种:静态函数1voidprint()2{3for(inti=0;i<5;i++)4qInfo()<<"helloglobalprint";5}6classMainWindow:publicQWidget7{8Q_OBJECT9public:10MainWindow(QWidget*parent=nullptr):QWidget(parent)......
  • Intel Lunar Lake失去超线程!但多核性能飙升1.5倍
    Intel将在今年晚些时候推出ArrowLake、LunarLake两套平台,工艺、架构基本相同,分别面向高性能和低功耗,一个意外变化就是不支持超线程。尽管如此,大家也不必为多核性能担忧。据最新曝料,17W功耗的LunarLake对比15W功耗的MeteorLake-U,CineBench23/5.4.5测试中,多核性能可提升几乎......
  • MySQL实现事务隔离的原理
    一、readview四个字段create_trx_id:创建该readview的事务的事务idm_ids:创建readview时,当前数据库中的活跃事务(指启动但还没提交的事务)min_trx_id:m_ids的最小值max_trx_id:创建readview后,下一个事务的id二、聚簇索引的隐藏列trx_id:最近一次改动该聚簇索引记录的事务idrol......
  • 线程池的使用场景
    在实际开发中,线程池用于优化线程的使用,提高系统性能,减少线程创建和销毁的开销,以及提供更高的系统稳定性。下面将详细解析几个常见的线程池使用场景,并结合源码和代码演示进行说明。场景一:Web应用的并发请求处理Web应用通常需要同时处理多个用户的请求。为了不每个请求都创建一......
  • 进程与线程
    进程与线程:进程:进程是操作系统资源分配的单位,其中存放dll,代码,堆,栈线程:调度单位多线程优点:1.提高响应能力:GUI程序,主线程操作UI,耗时操作放在工作线程中2.提高程序性能线程有哪些开销:空间上:1.数据结构上C#:Thread类......
  • 深入浅出Java多线程(十):CAS
    引言大家好,我是你们的老伙计秀才!今天带来的是[深入浅出Java多线程]系列的第十篇内容:CAS。大家觉得有用请点赞,喜欢请关注!秀才在此谢过大家了!!!在多线程编程中,对共享资源的安全访问和同步控制是至关重要的。传统的锁机制,如synchronized关键字和ReentrantLock等,能够有效防止多个线程......
  • 多线程系列(十六) -常用并发原子类详解
    一、简介在Java的java.util.concurrent包中,除了提供底层锁、并发同步等工具类以外,还提供了一组原子操作类,大多以Atomic开头,他们位于java.util.concurrent.atomic包下。所谓原子类操作,顾名思义,就是这个操作要么全部执行成功,要么全部执行失败,是保证并发编程安全的重要一环。相......
  • HashMap线程不安全实例(jdk1.7)
    一、前言jdk1.7中,以put()方法举例,线程不安全的一些情况:1,初始化HashMap的桶数组的时候,一个线程初始化了桶数组并插入了第一个元素,但是另一个线程不知道初始化好了,也执行了初始化的操作,清除了前面线程已经插入的元素;2,两个线程同时触发扩容,在翻转同个桶位上的链表时,链表形成环,类似......