自定义线程池

自定义线程池

什么是线程?

线程(Thread)是程序执行流的最小单元。一个进程可以包含多个线程,每个线程负责执行进程中的一部分任务。多线程可以让程序在同一时刻执行多个任务,提高程序的并发性和响应性。

为什么要使用线程

  • 提高程序的响应性:通过使用线程,可以将一些耗时的任务放到后台线程中执行,使得主线程不被阻塞,提高了程序的响应性。例如,在用户点击某个按钮时,主线程可以继续响应用户的输入,而耗时任务则可以在后台线程中执行;
  • 提高系统资源利用率:多线程可以充分利用多核处理器的性能,提高系统的资源利用率。每个线程可以在不同的处理器核心上并行执行,加快任务的处理速度;
  • 实现异步编程:线程可以用于实现异步编程,使得程序可以在等待某个任务完成的同时继续执行其他任务。这种方式可以提高程序的效率,特别是在处理I/O操作等耗时任务时;

为什么要使用线程池

  • 降低线程创建和销毁的开销: 线程的创建和销毁是比较昂贵的操作,会消耗系统的资源。使用线程池可以避免频繁地创建和销毁线程,提高了线程的重用性,降低了系统的开销;

  • 提供任务队列: 线程池通常包含一个任务队列,用于存储等待执行的任务。当线程池中的线程都在执行任务时,新的任务会被放入任务队列中等待执行,避免任务丢失;

    注意:极端情况下,如果任务队列已满且线程池中的线程也都在执行任务,新的任务将无法被存储在队列中,从而可能导致任务丢失。

  • 避免系统崩溃: 通过控制并发线程数量和提供任务队列,线程池可以避免系统因为线程过多导致崩溃或资源耗尽的情况;

线程池任务提交流程

线程池的拒绝策略

  1. AbortPolicy(默认) :当工作队列已满的情况下并且无法添加新的任务,抛出RejectedExecutionException异常
  2. CallerRunsPolicy :当没有可用的线程来执行任务时,提交任务的线程(通常是调用execute()的那个线程)将会执行该任务
  3. DiscardOldestPolicy :这个策略会尝试丢弃最早提交到线程池的任务(即队列中最旧的任务),以腾出空间给新提交的任务。如果这样做之后仍然无法添加新任务,则会退化为使用AbortPolicy
  4. DiscardPolicy: 这个策略简单地丢弃无法处理的任务,不做任何额外的通知。也就是说,任务将被静默地丢弃,不会有任何异常抛出

线程池的5种工作状态

线程池的7个核心参数

  1. corePoolSize
    • 核心线程数。线程池中保持的最小线程数。即使这些线程处于空闲状态,它们也不会被销毁,除非设置了allowCoreThreadTimeOuttrue
  2. maximumPoolSize
    • 最大线程数。线程池能够容纳的最大线程数。当活动线程数达到此值后,新的任务将会根据拒绝策略进行处理。
  3. keepAliveTime
    • 非核心线程的存活时间。当线程池中的线程数大于corePoolSize时,多余的空闲线程会等待的时间长度,在此期间内如果没有新的任务到来,多余的线程将被销毁。
  4. TimeUnit unit
    • keepAliveTime参数的时间单位。常用的有TimeUnit.SECONDSTimeUnit.MINUTES等。
  5. workQueue
    • 任务队列。这是一个阻塞队列,用来保存等待执行的任务。常见的队列类型包括ArrayBlockingQueueLinkedBlockingQueueSynchronousQueue等。
  6. threadFactory
    • 线程工厂。用于创建新线程的工厂,可以通过它来设置线程的名字、优先级等属性。
  7. handler
    • 拒绝策略。当线程池不能接收更多任务时(比如线程池已经达到了最大大小并且任务队列也满了),应该采用何种方式处理新到达的任务。可以选择的策略包括但不限于AbortPolicyCallerRunsPolicyDiscardOldestPolicyDiscardPolicy

配置线程池

java 复制代码
@Configuration
@EnableAsync
public class TheadPoolConfig {

    @Bean
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10); // 核心线程数
        executor.setMaxPoolSize(20); // 最大线程数
        executor.setQueueCapacity(100); // 队列容量
        executor.setThreadNamePrefix("WeblogThreadPool-"); // 线程名前缀
        executor.initialize();
        return executor;
    }

}

在上述配置中,@EnableAsync注解用于启用 Spring 的异步特性。ThreadPoolTaskExecutor 用于创建一个线程池,我们可以根据实际的业务场景来调整核心线程数、最大线程数、队列容量等参数。

java 复制代码
@Component
@Slf4j
public class ReadArticleSubscriber implements ApplicationListener<ReadArticleEvent> {

    @Autowired
    private ArticleMapper articleMapper;

    @Override
    @Async("threadPoolTaskExecutor")
    public void onApplicationEvent(ReadArticleEvent event) {
        // 在这里处理收到的事件,可以是任何逻辑操作
        Long articleId = event.getArticleId();

        // 获取当前线程名称
        String threadName = Thread.currentThread().getName();

        log.info("==> threadName: {}", threadName);
        log.info("==> 文章阅读事件消费成功,articleId: {}", articleId);

        articleMapper.increaseReadNum(articleId);
    }
}

需要异步的事件使用的@Async来实现异步操作

自定义线程的好处

默认线程池

Java的java.util.concurrent.Executors类提供了一些静态工厂方法来创建预定义的线程池。例如:

  • newFixedThreadPool(int nThreads):创建一个固定大小的线程池。
  • newSingleThreadExecutor():创建一个单线程化的线程池。
  • newCachedThreadPool():创建一个可缓存的线程池。
  • newScheduledThreadPool(int corePoolSize):创建一个支持定时及周期性任务执行的线程池。

可能会导致大量的线程创建,默认线程池的拒绝策略可能是简单地抛出异常或者静默失败,这不利于错误的诊断和处理

自定义线程池

自定义线程池允许开发者更精细地控制线程池的行为,包括但不限于以下方面:

  1. 线程数控制:可以根据应用的具体需求来设定核心线程数、最大线程数和线程存活时间。
  2. 任务队列:可以选择不同类型的队列来优化任务的排队和调度。
  3. 线程命名和优先级 :可以使用自定义的ThreadFactory来创建带有特定名称前缀和优先级的线程。
  4. 拒绝策略:可以根据业务逻辑实现特定的拒绝策略,而不是简单的丢弃或抛出异常。
  5. 资源管理:可以更好地控制资源的使用,避免因线程过多而耗尽系统资源。
好处:
  • 更好的性能:通过精确控制线程池的大小和其他参数,可以提高系统的响应时间和吞吐量。
  • 增强的健壮性 :通过实现适当的拒绝策略,可以在系统过载时提供更可靠的错误处理。
    制资源的使用,避免因线程过多而耗尽系统资源。
相关推荐
腥臭腐朽的日子熠熠生辉1 小时前
解决maven失效问题(现象:maven中只有jdk的工具包,没有springboot的包)
java·spring boot·maven
ejinxian1 小时前
Spring AI Alibaba 快速开发生成式 Java AI 应用
java·人工智能·spring
杉之1 小时前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
圈圈编码1 小时前
Spring Task 定时任务
java·前端·spring
俏布斯1 小时前
算法日常记录
java·算法·leetcode
27669582921 小时前
美团民宿 mtgsig 小程序 mtgsig1.2 分析
java·python·小程序·美团·mtgsig·mtgsig1.2·美团民宿
爱的叹息2 小时前
Java 连接 Redis 的驱动(Jedis、Lettuce、Redisson、Spring Data Redis)分类及对比
java·redis·spring
程序猿chen2 小时前
《JVM考古现场(十五):熵火燎原——从量子递归到热寂晶壁的代码涅槃》
java·jvm·git·后端·java-ee·区块链·量子计算
松韬2 小时前
Spring + Redisson:从 0 到 1 搭建高可用分布式缓存系统
java·redis·分布式·spring·缓存
绝顶少年3 小时前
Spring Boot 注解:深度解析与应用场景
java·spring boot·后端