自定义线程池

自定义线程池

什么是线程?

线程(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. 资源管理:可以更好地控制资源的使用,避免因线程过多而耗尽系统资源。
好处:
  • 更好的性能:通过精确控制线程池的大小和其他参数,可以提高系统的响应时间和吞吐量。
  • 增强的健壮性 :通过实现适当的拒绝策略,可以在系统过载时提供更可靠的错误处理。
    制资源的使用,避免因线程过多而耗尽系统资源。
相关推荐
小魏冬琅10 分钟前
探索面向对象的高级特性与设计模式(2/5)
java·开发语言
TT哇24 分钟前
【Java】数组的定义与使用
java·开发语言·笔记
look_outs42 分钟前
JavaSE笔记2】面向对象
java·开发语言
武子康1 小时前
大数据-191 Elasticsearch - ES 集群模式 配置启动 规划调优
java·大数据·elk·elasticsearch·搜索引擎·全文检索
A_aspectJ1 小时前
‌Spring MVC的主要组件有哪些?
java·spring·mvc
塔塔开!.1 小时前
Maven的依赖
java·maven
liuyang-neu1 小时前
力扣第420周赛 中等 3324. 出现在屏幕上的字符串序列
java·算法·leetcode
划]破1 小时前
Maven的安装及配置
java·maven
讓丄帝愛伱1 小时前
dependencyManagement保持maven的多模块依赖版本一致
java·maven
Ellie陈1 小时前
Java已死,大模型才是未来?
java·开发语言·前端·后端·python