spring task 和线程池研究

原文地址:http://blog.csdn.net/qihezhuanjia123/article/details/73604955

涉及如下内容

1、如何实现spring task定时任务的配置

2、task里面的一个job方法如何使用多线程,配置线程池

如何配置等待子线程结束后,再结束主线程

1、如何实现spring task定时任务的配置

因工作需要,需要定时执行一个方法,通过相关比较后,发现spring自带的task 可以满足,配置简单

步骤

1)增加配置文件 ,在applicationContext-cfg.xml 主配置文件里面添加 相关task标签

[html] view plain copy

<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"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd    
       http://www.springframework.org/schema/tx   
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd    
       http://www.springframework.org/schema/aop   
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd    
       http://www.springframework.org/schema/context   
       http://www.springframework.org/schema/context/spring-context-3.0.xsd  
       http://www.springframework.org/schema/task  
       http://www.springframework.org/schema/task/spring-task-3.0.xsd  
       http://www.springframework.org/schema/jee       
       http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">  

2)编写bean类和执行方法

编写jobService类,里面实现testjobThread方法,调用的spring注入过的action、service方法

[java] view plain copy

@Component("jobService")  
public class jobService  
{  
    private static Logger logger = Logger.getLogger(jobService.class);  

    @Autowired  
    private ThreadPoolTaskExecutor taskExecutor;  
    final CountDownLatch countDownLatch = new CountDownLatch(3);   

    /**  
    * @Title: DZFP_job  
    * @Description:开票定时任务 
    */  
    public void testjobThread()  
    {  
        Date startdate = new Date();  
        logger.info("DZFP_job_JOB 开始执行任务...,时间   " + startdate);  
        try  
        {  
            DzfpAction.Dzfp_SendAll();  
        }  
        catch (Exception e)  
        {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
            logger.error(StringUtil.grabExceptionMessage(e));  
        }  
        Date enddate = new Date();  
        logger.info("DZFP_job_JOB 任务完成...时间  " + enddate + "   耗时   " + String.valueOf(enddate.getTime() - startdate.getTime()) + "毫秒");  
    }  

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

pool-size=”5” 该参数主要解决,多个调度并行的问题,如下图5个task任务,建议设置3–5个调度

如果配置参数为 1,下面5个task任务会依次执行,如果一个时间超出,后面的任务一直在等待,影响业务

[html] view plain copy

<!-- 定时任务 -->  
<task:scheduler id="scheduler" pool-size="5" />  
<task:scheduled-tasks scheduler="scheduler">  
    <!-- 每天7点到7点55, 每隔5分钟执行一次 "0 0/5 7 * * ?"-->  
    <task:scheduled ref="jobService" method="DZFPgetInvoie_job" cron="0 0/30 * * * ?" />  
    <task:scheduled ref="jobService" method="DZFPgetInvoie_hong_job" cron="0 0/30 * * * ?" />  
       <span style="white-space:pre">   </span><task:scheduled ref="jobService" method="testjobThread" cron="0/5 * * * * ?" />  
    <task:scheduled ref="jobService" method="hzgd_job" cron="0/30 * * * * ?" />  
    <task:scheduled ref="jobService" method="alipay_pay_job" cron="0/30 * * * * ?" />  
</task:scheduled-tasks>  

使用以上配置后,启动项目就可以定时执行testjobThread方法里面的业务了。

2、task里面的一个job方法如何使用多线程,配置线程池

经过测试,spring task里面的方法是被串行执行的,比如上面配置的方法 testjobThread方法,5秒执行一次,如果有一个执行过程时间过长,后面的一次调度一直等上次执行结束后,才会启动下一次调用。

也就是说spring task是会监控 执行方法的主线程,如果主线程未结束的话,下一次就不会执行。

根据业务需求,这个testjobThread里面的 业务,需要多线程执行 (批量抽取数据)

spring框架里面,推荐使用线程池

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

[html] view plain copy

<!-- spring线程池-->             
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">  
    <!-- 线程池维护线程的最少数量 -->  
    <property name="corePoolSize" value="5" />  
    <!-- 线程池维护线程所允许的空闲时间,默认为60s  -->  
    <property name="keepAliveSeconds" value="200" />  
    <!-- 线程池维护线程的最大数量 -->  
    <property name="maxPoolSize" value="20" />  
    <!-- 缓存队列最大长度 -->  
    <property name="queueCapacity" value="20" />  
    <!-- 对拒绝task的处理策略   线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者-->  
    <property name="rejectedExecutionHandler">  
    <!-- AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常 -->  
        <!-- CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 -->  
        <!-- DiscardOldestPolicy:抛弃旧的任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->  
        <!-- DiscardPolicy:抛弃当前任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->  
        <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />  
    </property>  
    <property name="waitForTasksToCompleteOnShutdown" value="true" />  
</bean>  

2)修改业务操作类为thread类,实现run()方法

添加计数器CountDownLatch ,控制子线程结束后,再结束主线程

注意对象实现@Scope(“prototype”),用到了成员变量参数

[java] view plain copy

package cn.hao24.action;  
import java.util.Date;  
<name code="" class="java">import java.util.concurrent.CountDownLatch;  

import org.springframework.context.annotation.Scope;  
import org.springframework.stereotype.Component;  

import cn.hao24.util.DateUtil;  
import cn.hao24.util.SpringContextUtils;  

@Component("testThreadAction")  
@Scope("prototype")  
public class testThreadAction extends Thread  
{  

/** 
 * spring tash默认是单线程 串行执行,即一个方法执行完成前,后面的job不会执行的 
 * 但是如果主方法里面产生了thread线程, 主线程如果不等子线程结束后 就结束的话, task任务会产生多次调度 
 */  

    private String Treadname;  
    private CountDownLatch latch;  

    public testThreadAction(String Treadname,CountDownLatch latch){  
        this.Treadname=Treadname;  
        this.latch=latch;  
    }  

    @Override  
    public void run()  
    {  


        try  
        {  
            //主业务方法  
            for (int i = 0; i < 10; i++)  
            {  
                Thread current = Thread.currentThread();  
                System.out.println("线程号:"+current.getId() +"--"+current.getName()+" --"+Treadname +":---runing--- "+i+"--"+DateUtil.format(new Date(), "yyyyMMddHHmmss") );  
                Thread.sleep(20000);  
            }  
        }  
        catch (InterruptedException e)  
        {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }finally{  
            //设置实例 执行完毕  
            latch.countDown();  
        }  

    }  
    public void setTreadname(String treadname)  
    {  
        Treadname = treadname;  
    }  


    public void setLatch(CountDownLatch latch)  
    {  
        this.latch = latch;  
    }  


}</name>  

2)修改job调度的方法为多线程,配置3个线程
[java] view plain copy

package cn.hao24.job;  

import java.util.Date;  
import java.util.concurrent.CountDownLatch;  

import javax.annotation.Resource;  

import org.apache.log4j.Logger;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;  
import org.springframework.stereotype.Component;  

import cn.hao24.action.DzfpAction;  
import cn.hao24.action.HzgdAction;  
import cn.hao24.action.KJGOrderjob;  
import cn.hao24.action.testThreadAction;  
import cn.hao24.service.ZFBService;  
import cn.hao24.util.SpringContextUtils;  
import cn.hao24.util.StringUtil;  

@Component("jobService")  
public class jobService  
{  
    private static Logger logger = Logger.getLogger(jobService.class);  

    @Autowired  
    private ThreadPoolTaskExecutor taskExecutor;  
    final CountDownLatch countDownLatch = new CountDownLatch(3);   
    public void testjobThread()  
    {  
        try  
        {  
            CountDownLatch latch=new CountDownLatch(3);  //java工具类,类似与计数器,主要实现子线程未结束钱,主线程一直等待  
            testThreadAction test1 = (testThreadAction)SpringContextUtils.getBean("testThreadAction","test1",latch);  
            testThreadAction test2 = (testThreadAction)SpringContextUtils.getBean("testThreadAction","test2",latch);  
            testThreadAction test3 = (testThreadAction)SpringContextUtils.getBean("testThreadAction","test3",latch);  
            taskExecutor.execute(test1);  
            taskExecutor.execute(test2);  
            taskExecutor.execute(test3);  
            latch.await(); //子线程未结束前,一直等待  
            //test1.run();  
        }  
        catch (Exception e)  
        {  
            e.printStackTrace();  
            logger.error(StringUtil.grabExceptionMessage(e));  
        }  
    }  
}  

执行效果如下:

虽然 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

猜你喜欢

转载自blog.csdn.net/hp_yangpeng/article/details/79392890
今日推荐