zoukankan      html  css  js  c++  java
  • 线程池ThreadPoolTaskExecutor配置说明

         

    springboot中使用

    @Configuration
    @EnableAsync
    public class Test02 {
        @Bean("taskModuleExecutor")
        ThreadPoolTaskExecutor getCrawler1(){
            ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
            threadPoolTaskExecutor.setCorePoolSize(3);
            threadPoolTaskExecutor.setMaxPoolSize(10);
            threadPoolTaskExecutor.setQueueCapacity(200);
            threadPoolTaskExecutor.setThreadNamePrefix("task-concurrent-work");
            threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            threadPoolTaskExecutor.initialize();
            return threadPoolTaskExecutor;
        }
    
    }
       @Resource(name="taskModuleExecutor")
        private TaskExecutor taskExecutor;

    一般实际开发中经常用到多线程,所以需要使用线程池了,

     ThreadPoolTaskExecutor通常通过XML方式配置,或者通过Executors的工厂方法进行配置。 
     XML方式配置代码如下:交给spring来管理;

      <bean id="taskExecutor"
              class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
            <!-- 核心线程数 -->
            <property name="corePoolSize" value="4000" />
            <!-- 最大线程数 -->
            <property name="maxPoolSize" value="20000" />
            <!-- 队列最大长度 -->
            <property name="queueCapacity" value="2000" />
            <!-- 线程池维护线程所允许的空闲时间 -->
            <property name="keepAliveSeconds" value="30" />
            <!-- 线程池对拒绝任务(无线程可用)的处理策略 -->
            <property name="rejectedExecutionHandler">
                <bean class="java.util.concurrent.ThreadPoolExecutor$DiscardPolicy" />
            </property>
        </bean>

    rejectedExecutionHandler字段用于配置拒绝策略,常用的拒绝策略如下:

               AbortPolicy,用于被拒绝任务的处理程序,它将抛出RejectedExecutionException。

              CallerRunsPolicy,用于被拒绝任务的处理程序,它直接在execute方法的调用线程中运行被拒绝的任务。

              DiscardOldestPolicy,用于被拒绝任务的处理程序,它放弃最旧的未处理请求,然后重试execute

              DiscardPolicy,用于被拒绝任务的处理程序,默认情况下它将丢弃被拒绝的任务。


    提交任务

        无返回值的任务使用execute(Runnable)

        有返回值的任务使用submit(Runnable)


    案例代码 

     threadPoolTaskExecutor.execute(new Runnable() {
                    public void run() {
                        synchronized (Controller01.class) {
                            try {
                                HttpUtils.get("http://192.168.31.223:8085/test2.do");
                                System.out.println(System.currentTimeMillis());
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
    
    
                });

    任务处理流程:

    • 当一个任务被提交到线程池时,首先查看线程池的核心线程是否都在执行任务,否就选择一条线程执行任务,是就执行第二步。
    • 查看核心线程池是否已满,不满就创建一条线程执行任务,否则执行第三步。
    • 查看任务队列是否已满,不满就将任务存储在任务队列中,否则执行第四步。
    • 查看线程池是否已满,不满就创建一条线程执行任务,否则就按照策略处理无法执行的任务。

    在ThreadPoolExecutor中表现为:

    • 如果当前运行的线程数小于corePoolSize,那么就创建线程来执行任务(执行时需要获取全局锁)。
    • 如果运行的线程大于或等于corePoolSize,那么就把task加入BlockQueue。
    • 如果创建的线程数量大于BlockQueue的最大容量,那么创建新线程来执行该任务。
    • 如果创建线程导致当前运行的线程数超过maximumPoolSize,就根据饱和策略来拒绝该任务。

    关闭线程池

            调用shutdown或者shutdownNow,

            两者都不会接受新的任务,而且通过调用要停止线程的interrupt方法来中断线程,有可能线程永远不会被中断,

           不同之处在于shutdownNow会首先将线程池的状态设置为STOP,然后尝试停止所有线程(有可能导致部分任务没有执行完)然后返回未执行任务的列表。

           而shutdown则只是将线程池的状态设置为shutdown,然后中断所有没有执行 

           任务的线程,并将剩余的任务执行完。


    常用状态:

    • taskCount:线程需要执行的任务个数。
    • completedTaskCount:线程池在运行过程中已完成的任务数。
    • largestPoolSize:线程池曾经创建过的最大线程数量。
    • getPoolSize获取当前线程池的线程数量。
    • getActiveCount:获取活动的线程的数量

         通过继承线程池,重写beforeExecuteafterExecuteterminated方法来在线程执行任务前,线程执行任务结束,和线程终结前获取线程的运行情况,根据具体情况调整线程池的线程数量


     使用场景

          1、当你的任务是非必要的时候。比如记录操作日志、通知第三方服务非必要信息等,可以使用线程池处理非阻塞任务 
          2、当你的任务非常耗时时候,可以采用线程池技术 
          3、当请求并发很高时,可以采用线程池技术优化处理


        多线程是不是能加快处理速度?

             在使用多线程时,一定要知道一个道理:处理速度的最终决定因素是CPU、内存等,在单CPU(无论多少核)上,分配CPU资源的单位是“进程”而不是“线程”。

             我们可以做一个简单的试验:

             假设我要拷贝100万条数据,单CPU电脑,用一个进程,在单线程的情况下,CPU占用率为5%,耗时1000秒。那么当在这个进程下,开辟10个线程同时去运行,是不是CPU占用率增加到50%,耗时减少到100秒呢?显然不是。我实测出来的情况是这样的:

            “CPU占用率仍然是5%,总耗时仍然是1000秒。且每个线程的运行时间也为1000秒。”


    总结:

       第一,

          看硬件。如果是在比较强大的、多CPU的服务器上运行程序,可以使用多线程来提高并发数和执行速度。

          但是线程也不宜过多,即使是16个CPU的服务器,同一时间最多也只能真正意义上地并发处理16个线程,多出来的线程还是要等待。

    第二,

         看用途。如果你不在乎处理速度,仅仅是为了提高并发处理能力,那么理所当然地用多线程,

         但是如果你仅仅是想提高处理速度,且又是在单CPU机器上运行,那么多线程并不值得。

          如果你的任务很耗时,且可以一部分、一部分地做,那么最好不要用多线程(好比搬       砖,单线程一次搬10块,总共搬10天,但搬一块算一块,到第9天的时候,你就搬完90块砖了;

          如果你用10个线程同时去搬砖,同样要搬10天,但是到第9天的时候,这10个线程100块砖都“还在路上”,一块砖都没搬完!)。

  • 相关阅读:
    翻转单词顺序序列
    左旋转字符串
    查找第一个只出现一次的字符
    C语言函数与程序结构
    C语言实现快速排序法(分治法)
    C语言binsearch,shellsort,insertsort
    c语言的类型、运算符与表达式
    进程和线程
    CMS和G1
    Python IO编程
  • 原文地址:https://www.cnblogs.com/qq376324789/p/9729535.html
Copyright © 2011-2022 走看看