线程池ThreadPoolTaskExecutor配置说明

     通常实际开发中常常用到多线程,因此须要使用线程池了,java

 ThreadPoolTaskExecutor一般经过XML方式配置,或者经过Executors的工厂方法进行配置。 
 XML方式配置代码以下:交给spring来管理;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,用于被拒绝任务的处理程序,默认状况下它将丢弃被拒绝的任务。优化

 


 

提交任务spa

    无返回值的任务使用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方法来在线程执行任务前,线程执行任务结束,和线程终结前获取线程的运行状况,根据具体状况调整线程池的线程数量


 

 使用场景

 

      一、当你的任务是非必要的时候。好比记录操做日志、通知第三方服务非必要信息等,可使用线程池处理非阻塞任务 
      二、当你的任务很是耗时时候,能够采用线程池技术 
      三、当请求并发很高时,能够采用线程池技术优化处理

 


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

         在使用多线程时,必定要知道一个道理:处理速度的最终决定因素是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块砖都“还在路上”,一块砖都没搬完!)。

相关文章
相关标签/搜索