關於 Spring task和線程池

最近因工作需求,研究了一下spring task定時任務,和線程池,有了一定收穫,記錄一下

涉及如下內容

1、如何實現spring task定時任務的配置

2、task裏面的一個job方法如何使用多線程,配置線程池

    如何配置等待子線程結束後,再結束主線程


1、如何實現spring task定時任務的配置

因工作需要,需要定時執行一個方法,通過相關比較後,發現spring自帶的task 可以滿足,配置簡單

步驟

1)增加配置文件 ,在applicationContext-cfg.xml 主配置文件裏面添加 相關task標籤

  1. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:task="http://www.springframework.org/schema/task" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee"  
  2.     xsi:schemaLocation="http://www.springframework.org/schema/beans   
  3.        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd    
  4.        http://www.springframework.org/schema/tx   
  5.        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd    
  6.        http://www.springframework.org/schema/aop   
  7.        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd    
  8.        http://www.springframework.org/schema/context   
  9.        http://www.springframework.org/schema/context/spring-context-3.0.xsd  
  10.        http://www.springframework.org/schema/task  
  11.        http://www.springframework.org/schema/task/spring-task-3.0.xsd  
  12.        http://www.springframework.org/schema/jee       
  13.        http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">  

2)編寫bean類和執行方法  

編寫jobService類,裏面實現testjobThread方法,調用的spring注入過的action、service方法

  1. @Component("jobService")  
  2. public class jobService  
  3. {  
  4.     private static Logger logger = Logger.getLogger(jobService.class);  
  5.   
  6.     @Autowired  
  7.     private ThreadPoolTaskExecutor taskExecutor;  
  8.     final CountDownLatch countDownLatch = new CountDownLatch(3);   
  9.   
  10.     /**  
  11.     * @Title: DZFP_job  
  12.     * @Description:開票定時任務 
  13.     */  
  14.     public void testjobThread()  
  15.     {  
  16.         Date startdate = new Date();  
  17.         logger.info("DZFP_job_JOB 開始執行任務...,時間   " + startdate);  
  18.         try  
  19.         {  
  20.             DzfpAction.Dzfp_SendAll();  
  21.         }  
  22.         catch (Exception e)  
  23.         {  
  24.             // TODO Auto-generated catch block  
  25.             e.printStackTrace();  
  26.             logger.error(StringUtil.grabExceptionMessage(e));  
  27.         }  
  28.         Date enddate = new Date();  
  29.         logger.info("DZFP_job_JOB 任務完成...時間  " + enddate + "   耗時   " + String.valueOf(enddate.getTime() - startdate.getTime()) + "毫秒");  
  30.     }  


3)配置task相關配置文件,在文件applicationContext-cfg.xml 中增加下列內容

pool-size="5"  該參數主要解決,多個調度並行的問題,如下圖5個task任務,建議設置3--5個調度

如果配置參數爲 1,下面5個task任務會依次執行,如果一個時間超出,後面的任務一直在等待,影響業務

  1. <!-- 定時任務 -->  
  2. <task:scheduler id="scheduler" pool-size="5" />  
  3. <task:scheduled-tasks scheduler="scheduler">  
  4.     <!-- 每天7點到7點55, 每隔5分鐘執行一次 "0 0/5 7 * * ?"-->  
  5.     <task:scheduled ref="jobService" method="DZFPgetInvoie_job" cron="0 0/30 * * * ?" />  
  6.     <task:scheduled ref="jobService" method="DZFPgetInvoie_hong_job" cron="0 0/30 * * * ?" />  
  7.        <span style="white-space:pre;">  </span><task:scheduled ref="jobService" method="testjobThread" cron="0/5 * * * * ?" />  
  8.     <task:scheduled ref="jobService" method="hzgd_job" cron="0/30 * * * * ?" />  
  9.     <task:scheduled ref="jobService" method="alipay_pay_job" cron="0/30 * * * * ?" />  
  10. </task:scheduled-tasks>  

使用以上配置後,啓動項目就可以定時執行testjobThread方法裏面的業務了。


2、task裏面的一個job方法如何使用多線程,配置線程池

經過測試,spring task裏面的方法是被串行執行的,比如上面配置的方法 testjobThread方法,5秒執行一次,如果有一個執行過程時間過長,後面的一次調度一直等上次執行結束後,纔會啓動下一次調用。

也就是說spring task是會監控 執行方法的主線程,如果主線程未結束的話,下一次就不會執行。

根據業務需求,這個testjobThread裏面的 業務,需要多線程執行 (批量抽取數據)

spring框架裏面,推薦使用線程池

1)配置線程池     在applicationContext-cfg.xml文件中增加配置如下

  1. <!-- spring線程池-->             
  2. <bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">  
  3.     <!-- 線程池維護線程的最少數量 -->  
  4.     <property name="corePoolSize" value="5" />  
  5.     <!-- 線程池維護線程所允許的空閒時間,默認爲60s  -->  
  6.     <property name="keepAliveSeconds" value="200" />  
  7.     <!-- 線程池維護線程的最大數量 -->  
  8.     <property name="maxPoolSize" value="20" />  
  9.     <!-- 緩存隊列最大長度 -->  
  10.     <property name="queueCapacity" value="20" />  
  11.     <!-- 對拒絕task的處理策略   線程池對拒絕任務(無線程可用)的處理策略,目前只支持AbortPolicy、CallerRunsPolicy;默認爲後者-->  
  12.     <property name="rejectedExecutionHandler">  
  13.     <!-- AbortPolicy:直接拋出java.util.concurrent.RejectedExecutionException異常 -->  
  14.         <!-- CallerRunsPolicy:主線程直接執行該任務,執行完之後嘗試添加下一個任務到線程池中,可以有效降低向線程池內添加任務的速度 -->  
  15.         <!-- DiscardOldestPolicy:拋棄舊的任務、暫不支持;會導致被丟棄的任務無法再次被執行 -->  
  16.         <!-- DiscardPolicy:拋棄當前任務、暫不支持;會導致被丟棄的任務無法再次被執行 -->  
  17.         <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />  
  18.     </property>  
  19.     <property name="waitForTasksToCompleteOnShutdown" value="true" />  
  20. </bean>  



2)修改業務操作類爲thread類,實現run()方法

添加計數器CountDownLatch ,控制子線程結束後,再結束主線程

注意對象實現@Scope("prototype"),用到了成員變量參數


  1. package cn.hao24.action;  
  2. import java.util.Date;  
  3. import java.util.concurrent.CountDownLatch;  
  4.   
  5. import org.springframework.context.annotation.Scope;  
  6. import org.springframework.stereotype.Component;  
  7.   
  8. import cn.hao24.util.DateUtil;  
  9. import cn.hao24.util.SpringContextUtils;  
  10.   
  11. @Component("testThreadAction")  
  12. @Scope("prototype")  
  13. public class testThreadAction extends Thread  
  14. {  
  15.   
  16. /** 
  17.  * spring tash默認是單線程 串行執行,即一個方法執行完成前,後面的job不會執行的 
  18.  * 但是如果主方法裏面產生了thread線程, 主線程如果不等子線程結束後 就結束的話, task任務會產生多次調度 
  19.  */  
  20.   
  21.     private String Treadname;  
  22.     private CountDownLatch latch;  
  23.       
  24.     public testThreadAction(String Treadname,CountDownLatch latch){  
  25.         this.Treadname=Treadname;  
  26.         this.latch=latch;  
  27.     }  
  28.       
  29.     @Override  
  30.     public void run()  
  31.     {  
  32.   
  33.              
  34.         try  
  35.         {  
  36.             //主業務方法  
  37.             for (int i = 0; i < 10; i++)  
  38.             {  
  39.                 Thread current = Thread.currentThread();  
  40.                 System.out.println("線程號:"+current.getId() +"--"+current.getName()+" --"+Treadname +":---runing--- "+i+"--"+DateUtil.format(new Date(), "yyyyMMddHHmmss") );  
  41.                 Thread.sleep(20000);  
  42.             }  
  43.         }  
  44.         catch (InterruptedException e)  
  45.         {  
  46.             // TODO Auto-generated catch block  
  47.             e.printStackTrace();  
  48.         }finally{  
  49.             //設置實例 執行完畢  
  50.             latch.countDown();  
  51.         }  
  52.                 
  53.     }  
  54.     public void setTreadname(String treadname)  
  55.     {  
  56.         Treadname = treadname;  
  57.     }  
  58.   
  59.   
  60.     public void setLatch(CountDownLatch latch)  
  61.     {  
  62.         this.latch = latch;  
  63.     }  
  64.       
  65.   
  66. }  
2)修改job調度的方法爲多線程,配置3個線程
  1. package cn.hao24.job;  
  2.   
  3. import java.util.Date;  
  4. import java.util.concurrent.CountDownLatch;  
  5.   
  6. import javax.annotation.Resource;  
  7.   
  8. import org.apache.log4j.Logger;  
  9. import org.springframework.beans.factory.annotation.Autowired;  
  10. import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;  
  11. import org.springframework.stereotype.Component;  
  12.   
  13. import cn.hao24.action.DzfpAction;  
  14. import cn.hao24.action.HzgdAction;  
  15. import cn.hao24.action.KJGOrderjob;  
  16. import cn.hao24.action.testThreadAction;  
  17. import cn.hao24.service.ZFBService;  
  18. import cn.hao24.util.SpringContextUtils;  
  19. import cn.hao24.util.StringUtil;  
  20.   
  21. @Component("jobService")  
  22. public class jobService  
  23. {  
  24.     private static Logger logger = Logger.getLogger(jobService.class);  
  25.   
  26.     @Autowired  
  27.     private ThreadPoolTaskExecutor taskExecutor;  
  28.     final CountDownLatch countDownLatch = new CountDownLatch(3);   
  29.     public void testjobThread()  
  30.     {  
  31.         try  
  32.         {  
  33.             CountDownLatch latch=new CountDownLatch(3);  //java工具類,類似與計數器,主要實現子線程未結束錢,主線程一直等待  
  34.             testThreadAction test1 = (testThreadAction)SpringContextUtils.getBean("testThreadAction","test1",latch);  
  35.             testThreadAction test2 = (testThreadAction)SpringContextUtils.getBean("testThreadAction","test2",latch);  
  36.             testThreadAction test3 = (testThreadAction)SpringContextUtils.getBean("testThreadAction","test3",latch);  
  37.             taskExecutor.execute(test1);  
  38.             taskExecutor.execute(test2);  
  39.             taskExecutor.execute(test3);  
  40.             latch.await(); //子線程未結束前,一直等待  
  41.             //test1.run();  
  42.         }  
  43.         catch (Exception e)  
  44.         {  
  45.             e.printStackTrace();  
  46.             logger.error(StringUtil.grabExceptionMessage(e));  
  47.         }  
  48.     }  
  49. }  

執行效果如下:

雖然  testjobThread  5秒執行一次,但是因爲使用到了 latch.await()   latch.countDown();需要等子線程執行完畢,纔會進行下一次job

子線程每次循環,會sleep 20秒,從下面結果看,3個線程 每隔20秒纔打印一次。符合最終要求

線程號:29--taskExecutor-3 --test3:---runing--- 0--20170622145500
線程號:28--taskExecutor-2 --test2:---runing--- 0--20170622145500
線程號:27--taskExecutor-1 --test1:---runing--- 0--20170622145500
線程號:28--taskExecutor-2 --test2:---runing--- 1--20170622145520
線程號:27--taskExecutor-1 --test1:---runing--- 1--20170622145520
線程號:29--taskExecutor-3 --test3:---runing--- 1--20170622145520
線程號:29--taskExecutor-3 --test3:---runing--- 2--20170622145540
線程號:28--taskExecutor-2 --test2:---runing--- 2--20170622145540
線程號:27--taskExecutor-1 --test1:---runing--- 2--20170622145540





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章