为什么要使用线程池

Posted 甜瓜瓜哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么要使用线程池相关的知识,希望对你有一定的参考价值。

Java线程的创建非常昂贵,需要JVMOS(操作系统)配合完成大量的工作:
(1)必须为线程堆栈分配和初始化大量内存块,其中包含至少1MB的栈内存。
(2)需要进行系统调用,以便在OS(操作系统)中创建和注册本地线程。

由于创建线程和销毁线程的代价比较高,为了避免销毁和创建时带来的性能损耗,所以我们使用线程将其缓存起来。

Java高并发应用频繁创建和销毁线程的操作是非常低效的,而且是不被编程规范所允许的。

如何降低Java线程的创建成本必须使用到线程池。线程池主要解决了以下两个问题:
(1)提升性能:线程池能独立负责线程的创建、维护和分配。在执行大量异步任务时,可以不需要自己创建线程,而是将任务交给线程池去调度。线程池能尽可能使用空闲的线程去执行异步任务,最大限度地对已经创建的线程进行复用,使得性能提升明显。

(2)线程管理:每个java线程池会保持一些基本的线程统计信息,例如完成的任务数量、空闲时间等,以便对线程进行有效管理,使得能对所接收到的异步任务进行高效调度

参考资料:为什么要使用线程池?

线程池正确打开方式- 拌饭使用效果更佳

为什么要用线程池?什么是线程池?怎么使用?素质三连!!!

  1. 为什么要用线程池?
    1. 节约系统资源。避免反复创建销毁线程
    2. 提供异步操作。提高响应速度
    3. 方便管理线程
  2. 什么是线程池?
    1. 多线程使用的一种管理方式。池化技术能有效的利用和管理一些资源
  3. 怎么使用?
    1. 当然是配合springboot使用。皮一下??
    2. 整个过程梳理如下
      1. 让IOC容器加载和管理线程池 - 即配置完成之后,项目启动便加载配置
      2. 调用时使用注解 - 方便开发

线程池核心概念介绍

  • corePoolSize 正常持有的线程数。在使用线程时,如果池中的线程数小于该数,会创建新线程
  • maximumPoolSize  允许拥有的最大线程数。 池中核心线程core如果超出了,会放到阻塞队列中。队列如果也满了,会申请创建线程但数量不能超过该数值。如果超过了会执行拒绝策略
  • BlockingQueue<Runnable> workQueue 阻塞队列,用于存放等待资源的线程。
  • long keepAliveTime,TimeUnit unit  大于核心线程数的线程空闲之后的存活时间。活着浪费空气

实际配置如下      

  1. 启动类上添加@EnableConfigurationProperties 注解。用于解析自定义配置属性。同时需要引入pom配置
    1. @EnableConfigurationProperties
      public class MerchantApplication {
          public static void main(String[] args) {
              SpringApplication.run(MerchantApplication.class, args);
          }
      }
      
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-configuration-processor</artifactId>
          <optional>true</optional>
      </dependency>
  2. 配置启动加载时需要的类。1.yml配置属性。2.配置类启动加载

    1. // yml配置
      task:
        pool:
          corePoolSize: 5
          queueCapacity: 20
          maxPoolSize: 10
          keepAliveSeconds: 3
      
      // 属性映射类
      @Data
      @ConfigurationProperties(prefix = "task.pool")
      @Component
      public class TaskThreadPoolProperty {
      
          private int corePoolSize;
      
          private int maxPoolSize;
      
          private int keepAliveSeconds;
      
          private int queueCapacity;
      
      }

      // 启动加载
      @Configuration
      @EnableAsync
      public class ThreadPoolConfig {

      @Resource
      private TaskThreadPoolProperty config;

      /**
      * 默认使用 名为 taskExecutor 的线程池
      *
      * @return 初始化线程池
      */
      @Bean("taskExecutor")
      public Executor configTaskAsyncPool() {
      ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
      //核心线程池大小
      executor.setCorePoolSize(config.getCorePoolSize());
      //最大线程数
      executor.setMaxPoolSize(config.getMaxPoolSize());
      //队列容量
      executor.setQueueCapacity(config.getQueueCapacity());
      //活跃时间
      executor.setKeepAliveSeconds(config.getKeepAliveSeconds());
      //线程名字前缀
      executor.setThreadNamePrefix("Qc-");
      // CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
      executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
      executor.setWaitForTasksToCompleteOnShutdown(true);
      return executor;
      }

      }  
  3. 调用 使用@Async注解调用
    1. // 实现异步操作的类
      @Component
      public class AsyncMessage {
      
          @Async
          public void sendMsgCL(String mobile, String content) {
              try {
                  Thread.sleep(3000L);
                  System.out.println("多线程异步执行" + "  " + Thread.currentThread().getName());
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }
      
      // 测试异步的接口
        @Resource
          private AsyncMessage message;
      
          @PostMapping("/data1")
          public void data() {
              message.sendMsgCL(null, null);
              System.out.println("haha");
          }         
        
  4. 注意点(如果不注意可能会让你多吃两碗)
    1. 配置yml自定义属性之后需要重启IDEA才能生效。
    2. 启动加载的bean命名为@Bean("taskExecutor") 。spring在调用线程池时默认的池子名称为taskExecutor
    3. 使用@Async注解可以指明使用的线程池
      1. 不能修饰静态static方法
      2. 异步类需要被IOC容器管理。即要使用注解类似@Component @Service 。。。等等
      3. 异步方法不能与被调用的异步方法在同一个类中
      4. @EnableAsync 记得加上。可以在启动类上也可以在配置类上。

 

 

以上是关于为什么要使用线程池的主要内容,如果未能解决你的问题,请参考以下文章

Java多线程和线程池(转)

Java多线程和线程池

JAVA线程池学习以及队列拒绝策略

JVM——深入解析之初识

JVM——深入解析之初识

要成为一个 Java 架构师得学习哪些知识?