关于Java中的线程池

关于线程池

今天总结一下线程池😘

Java中的线程池相当于把很多线程放到一个池子里,随用随取,避免频繁的创建和销毁线程造成的资源损耗和提高响应速度,而且线程池可以根据需要动态地调整线程数量,以适应不同的工作负载,从而提高系统的灵活性和性能

线程池参数

先讲一下面试爱问的7大线程池参数💕:

arduino 复制代码
 public ThreadPoolExecutor(int corePoolSize,
                           int maximumPoolSize,
                           long keepAliveTime,
                           TimeUnit unit,
                           BlockingQueue<Runnable> workQueue,
                           ThreadFactory threadFactory,
                           RejectedExecutionHandler handler) {...}
  • corePoolSize: 线程池中的核心线程数量,如果没有全局设置池内线程的过期时间,池内会维持此数量线程。
  • maximumPoolSize: 线程池中的最大线程数量,当核心线程都在运行任务,并且阻塞队列中任务数量已满,此时会创建非核心线程。
  • keepAliveTime & unit: 线程池中线程过期时间以及时间单位。
  • workQueue: 存放线程池内任务的阻塞队列,如 ArrayBlockingQueue、LinkedBlockingQueue...
  • threadFactory: 创建线程池中线程的线程工厂,可以在创建线程时初始化优先级、名称、守护状态...
  • handler: 当线程池中全部线程都在运行,阻塞队列也满的时候,会将添加的任务执行拒绝策略,JDK 线程池中实现了四种拒绝策略,默认 AbortPolicy,抛出异常。

简单来讲就是先找核心线程数,如果核心线程数满了,就进去阻塞队列,等着来找核心线程,如果队列都满了,那就使用非核心线程来处理,如果都超过最大线程数了,就使用拒绝策略具体处理

魔改一下?😎

如果我们想要线程池快速消费呢?

那看一下线程池消费的过程,有个排队去找核心线程数的过程,这个排队就造成时间浪费了

那么能不能改一下让请求来了发现核心线程数满了,直接去找非核心线程去执行,是不是相当于速度快了

Dubbo中的实现😶‍🌫️

其实Dubbo中早有其影子🙌🙌🙌

首先Dubbo中自定义了一个TaskQueue 阻塞队列扩展了LinkedBlockingQueue

scala 复制代码
 public class TaskQueue<R extends Runnable> extends LinkedBlockingQueue<Runnable> {
         ...
     // 队列中持有线程池的引用
     private EagerThreadPoolExecutor executor;
 ​
     public TaskQueue(int capacity) {
         super(capacity);
     }
 ​
     public void setExecutor(EagerThreadPoolExecutor exec) {
         executor = exec;
     }
 ​
     @Override
     public boolean offer(Runnable runnable) {
                 ...
         // 获取线程池中线程数
         int currentPoolThreadSize = executor.getPoolSize();
         // 如果有核心线程正在空闲,将任务加入阻塞队列,由核心线程进行处理任务
         if (executor.getSubmittedTaskCount() < currentPoolThreadSize) {
             return super.offer(runnable);
         }
 ​
         /**
          *【重点】当前线程池线程数量小于最大线程数
          * 返回false,根据线程池源码,会创建非核心线程
          */
         if (currentPoolThreadSize < executor.getMaximumPoolSize()) {
             return false;
         }
 ​
         // 如果当前线程池数量大于最大线程数,任务加入阻塞队列
         return super.offer(runnable);
     }
 }

慢慢来看😊,这段代码中,意思是线程来了先去找核心线程,如果核心满了,看是否小于最大线程数,小于的话,为了实现快速消费的目的,就直接失败,大于最大线程数的话,就说明当前要处理的线程比较多,就都放入阻塞队列慢慢消费

另一方面:

Dubbo中的EagerThreadPoolExecutor 封装了线程池,对原有的ThreadPoolExecutor类进行了扩展:

java 复制代码
 public class EagerThreadPoolExecutor extends ThreadPoolExecutor {
 ​
     /**
      * task count
      */
     private final AtomicInteger submittedTaskCount = new AtomicInteger(0);
 ​
     /**
      * @return current tasks which are executed
      */
     public int getSubmittedTaskCount() {
         return submittedTaskCount.get();
     }
 ​
     @Override
     protected void afterExecute(Runnable r, Throwable t) {
         submittedTaskCount.decrementAndGet();
     }
 ​
     @Override
     public void execute(Runnable command) {
         if (command == null) {
             throw new NullPointerException();
         }
         // do not increment in method beforeExecute!
         submittedTaskCount.incrementAndGet();
         try {
             super.execute(command);
         } catch (RejectedExecutionException rx) {
             // retry to offer the task into queue.
             final TaskQueue queue = (TaskQueue) super.getQueue();
             try {
                 if (!queue.retryOffer(command, 0, TimeUnit.MILLISECONDS)) {
                     submittedTaskCount.decrementAndGet();
                     throw new RejectedExecutionException("Queue capacity is full.", rx);
                 }
             } catch (InterruptedException x) {
                 submittedTaskCount.decrementAndGet();
                 throw new RejectedExecutionException(x);
             }
         } catch (Throwable t) {
             // decrease any way
             submittedTaskCount.decrementAndGet();
             throw t;
         }
     }
 }

我知道你很急,但是你先别急🙌🙌🙌

慢慢看,它主要是重写了其中的execute方法,它在里面定义了一个原子整数,用于记录已提交的任务数量

首先会对任务数量进行递增,然后尝试执行任务。如果任务执行被拒绝,会尝试将任务重新放入队列中。无论任务执行成功与否,已提交的任务数量都会递减

这样就能保证在高并发情况下 TaskQueue#offer(Runnable runnable) 做出逻辑处理

OK!完美,总的来说,不是很难💕💕💕

相关推荐
Adolf_19933 分钟前
Flask-JWT-Extended登录验证, 不用自定义
后端·python·flask
叫我:松哥15 分钟前
基于Python flask的医院管理学院,医生能够增加/删除/修改/删除病人的数据信息,有可视化分析
javascript·后端·python·mysql·信息可视化·flask·bootstrap
海里真的有鱼18 分钟前
Spring Boot 项目中整合 RabbitMQ,使用死信队列(Dead Letter Exchange, DLX)实现延迟队列功能
开发语言·后端·rabbitmq
工业甲酰苯胺29 分钟前
Spring Boot 整合 MyBatis 的详细步骤(两种方式)
spring boot·后端·mybatis
新知图书1 小时前
Rust编程的作用域与所有权
开发语言·后端·rust
wn5312 小时前
【Go - 类型断言】
服务器·开发语言·后端·golang
希冀1232 小时前
【操作系统】1.2操作系统的发展与分类
后端
GoppViper3 小时前
golang学习笔记29——golang 中如何将 GitHub 最新提交的版本设置为 v1.0.0
笔记·git·后端·学习·golang·github·源代码管理
爱上语文4 小时前
Springboot的三层架构
java·开发语言·spring boot·后端·spring
serve the people4 小时前
springboot 单独新建一个文件实时写数据,当文件大于100M时按照日期时间做文件名进行归档
java·spring boot·后端