Spring——Job線程池

  • Spring框架提供了線程池和定時(shí)任務(wù)執(zhí)行的抽象接口:TaskExecutor和TaskScheduler來(lái)支持異步執(zhí)行任務(wù)和定時(shí)執(zhí)行任務(wù)功能疤苹。
  • 同時(shí)使用框架自己定義的抽象接口來(lái)屏蔽掉底層JDK版本間以及Java EE中的線程池和定時(shí)任務(wù)處理的差異。 為啥懂缕?因?yàn)镾pring想要替你管理線程池的初始化、注解使用、停止等工作。
  • 另外Spring還支持集成JDK內(nèi)部的定時(shí)器Timer和Quartz Scheduler框架燎字。
  • 這里我們單說(shuō)TaskExecutor和TaskScheduler

ThreadPoolTaskExecutor

配置

<!-- spring thread pool executor -->           
    <bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <!-- 線程池維護(hù)線程的最少數(shù)量 -->
        <property name="corePoolSize" value="5" />
        <!-- 允許的空閑時(shí)間 -->
        <property name="keepAliveSeconds" value="200" />
        <!-- 線程池維護(hù)線程的最大數(shù)量 -->
        <property name="maxPoolSize" value="10" />
        <!-- 緩存隊(duì)列 -->
        <property name="queueCapacity" value="20" />
        <!-- 對(duì)拒絕task的處理策略 -->
        <property name="rejectedExecutionHandler">
            <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
        </property>
    </bean>

execute(Runable)方法執(zhí)行過程 (與JDK的線程池一樣機(jī)制)

  • 如果此時(shí)線程池中的數(shù)量小于corePoolSize,即使線程池中的線程都處于空閑狀態(tài)阿宅,也要?jiǎng)?chuàng)建新的線程來(lái)處理被添加的任務(wù)候衍。

如果此時(shí)線程池中的數(shù)量等于 corePoolSize,但是緩沖隊(duì)列 workQueue未滿洒放,那么任務(wù)被放入緩沖隊(duì)列蛉鹿。

如果此時(shí)線程池中的數(shù)量大于corePoolSize,緩沖隊(duì)列workQueue滿往湿,并且線程池中的數(shù)量小于maxPoolSize妖异,建新的線程來(lái)處理被添加的任務(wù)。

如果此時(shí)線程池中的數(shù)量大于corePoolSize领追,緩沖隊(duì)列workQueue滿他膳,并且線程池中的數(shù)量等于maxPoolSize,那么通過handler所指定的策略來(lái)處理此任務(wù)绒窑。也就是:處理任務(wù)的優(yōu)先級(jí)為:核心線程corePoolSize棕孙、任務(wù)隊(duì)列workQueue、最大線程 maximumPoolSize些膨,如果三者都滿了蟀俊,使用handler處理被拒絕的任務(wù)。

當(dāng)線程池中的線程數(shù)量大于corePoolSize時(shí)订雾,如果某線程空閑時(shí)間超過keepAliveTime肢预,線程將被終止。這樣葬燎,線程池可以動(dòng)態(tài)的調(diào)整池中的線程數(shù)误甚。

測(cè)試使用

@Component
public class TTaskThreadPool {

    @Autowired
    private TaskExecutor taskExecutor;

    public void testGo(){
        for (int i = 0; i < 100; i++) {
            final int num = i;
            this.taskExecutor.execute(()->{
                int idMe = num;
                System.out.println("before idMe = " + idMe);
                try {
                    Thread.sleep(2000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("after idMe = " + idMe);
            });
        }
    }
}

TaskExecutor types

There are a number of pre-built implementations of TaskExecutor included with the Spring distribution. In all likelihood, you shouldn’t ever need to implement your own.

  • SimpleAsyncTaskExecutor This implementation does not reuse any threads, rather it starts up a new thread for each invocation. However, it does support a concurrency limit which will block any invocations that are over the limit until a slot has been freed up. If you are looking for true pooling, see the discussions of SimpleThreadPoolTaskExecutor and ThreadPoolTaskExecutor below.

  • SyncTaskExecutor This implementation doesn’t execute invocations asynchronously. Instead, each invocation takes place in the calling thread. It is primarily used in situations where multi-threading isn’t necessary such as simple test cases.

  • ConcurrentTaskExecutor This implementation is an adapter for a java.util.concurrent.Executor object. There is an alternative, ThreadPoolTaskExecutor, that exposes the Executor configuration parameters as bean properties. It is rare to need to use the ConcurrentTaskExecutor, but if the ThreadPoolTaskExecutor isn’t flexible enough for your needs, the ConcurrentTaskExecutor is an alternative.

  • SimpleThreadPoolTaskExecutor This implementation is actually a subclass of Quartz’s SimpleThreadPool which listens to Spring’s lifecycle callbacks. This is typically used when you have a thread pool that may need to be shared by both Quartz and non-Quartz components.

  • ThreadPoolTaskExecutor This implementation is the most commonly used one. It exposes bean properties for configuring a java.util.concurrent.ThreadPoolExecutor and wraps it in a TaskExecutor. If you need to adapt to a different kind of java.util.concurrent.Executor, it is recommended that you use a ConcurrentTaskExecutor instead.

  • WorkManagerTaskExecutor

TaskScheduler

In addition to the TaskExecutor abstraction, Spring 3.0 introduces a TaskScheduler with a variety of methods for scheduling tasks to run at some point in the future.

public interface TaskScheduler {

    ScheduledFuture schedule(Runnable task, Trigger trigger);

    ScheduledFuture schedule(Runnable task, Date startTime);

    ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);

    ScheduledFuture scheduleAtFixedRate(Runnable task, long period);

    ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);

    ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);

}

Spring also features integration classes for supporting scheduling with the Timer, part of the JDK since 1.3, and the Quartz Scheduler ( http://quartz-scheduler.org). Both of those schedulers are set up using a FactoryBean with optional references toTimer or Trigger instances, respectively. Furthermore, a convenience class for both the Quartz Scheduler and the Timer is available that allows you to invoke a method of an existing target object

首先給出幾個(gè)結(jié)論:

  • 調(diào)度器本質(zhì)上還是通過juc的ScheduledExecutorService進(jìn)行的
  • 調(diào)度器啟動(dòng)后你無(wú)法通過修改系統(tǒng)時(shí)間達(dá)到讓它馬上執(zhí)行的效果
  • 被@Schedule注解的方法如果有任何Throwable出現(xiàn), 不會(huì)中斷后續(xù)Task, 只會(huì)打印Error日志

配置

<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="5"/>
<task:scheduler id="myScheduler" pool-size="10"/>

使用

在 Spring 管理的 Bean 中的方法上使用 Scheduled 注解。

@Scheduled(fixedDelay = 1000 * 60)
    public void checkAndUpdateRegisterZset() {
        //Do something
    }

Trigger interface

The basic idea of the Trigger is that execution times may be determined based on past execution outcomes or even arbitrary conditions. If these determinations do take into account the outcome of the preceding execution, that information is available within a TriggerContext.
因?yàn)槎〞r(shí)任務(wù)需要記錄任務(wù)的前后執(zhí)行時(shí)間點(diǎn)序列谱净,如果后續(xù)修改了系統(tǒng)時(shí)間窑邦,則會(huì)破壞既定時(shí)間序列,故需要記錄最開始預(yù)訂好的時(shí)間序列壕探。

public interface Trigger {
    // 獲取下一個(gè)執(zhí)行時(shí)間
    Date nextExecutionTime(TriggerContext triggerContext);
}
// 相對(duì)時(shí)間記錄載體
public interface TriggerContext {
    Date lastScheduledExecutionTime();
    Date lastActualExecutionTime();
    Date lastCompletionTime();
}

Trigger implementations

如常用的一個(gè)表達(dá)式實(shí)現(xiàn):
scheduler.schedule(task, new CronTrigger("0 15 9-17 * * MON-FRI"));

Annotation Support for Scheduling and Asynchronous Execution

Enable scheduling annotations

To enable support for @Scheduled and @Async annotations add @EnableScheduling and @EnableAsync to one of your @Configuration classes:

@Configuration
@EnableAsync
@EnableScheduling
public class AppConfig {
}

You are free to pick and choose the relevant annotations for your application. For example, if you only need support for @Scheduled, simply omit @EnableAsync. For more fine-grained control you can additionally implement the SchedulingConfigurer and/or AsyncConfigurer interfaces. See the javadocs for full details.

If you prefer XML configuration use the <task:annotation-driven> element.

<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="5"/>
<task:scheduler id="myScheduler" pool-size="10"/>

Notice with the above XML that an executor reference is provided for handling those tasks that correspond to methods with the @Async annotation, and the scheduler reference is provided for managing those methods annotated with @Scheduled.

@Scheduled annotation

The @Scheduled annotation can be added to a method along with trigger metadata. For example, the following method would be invoked every 5 seconds with a fixed delay, meaning that the period will be measured from the completion time of each preceding invocation.

@Scheduled(fixedDelay=5000)
public void doSomething() {
    // something that should execute periodically
}

@Scheduled(fixedRate=5000)
public void doSomething() {
    // something that should execute periodically
}

@Scheduled(initialDelay=1000, fixedRate=5000)
public void doSomething() {
    // something that should execute periodically
}

@Scheduled(cron="*/5 * * * * MON-FRI")
public void doSomething() {
    // something that should execute on weekdays only
}

Notice that the methods to be scheduled must have void returns and must not expect any arguments. If the method needs to interact with other objects from the Application Context, then those would typically have been provided through dependency injection.

As of Spring Framework 4.3, @Scheduled methods are supported on beans of any scope.

Make sure that you are not initializing multiple instances of the same @Scheduled annotation class at runtime, unless you do want to schedule callbacks to each such instance. Related to this, make sure that you do not use @Configurable on bean classes which are annotated with @Scheduled and registered as regular Spring beans with the container: You would get double initialization otherwise, once through the container and once through the @Configurable aspect, with the consequence of each @Scheduled method being invoked twice.

@Async annotation

The @Async annotation can be provided on a method so that invocation of that method will occur asynchronously. In other words, the caller will return immediately upon invocation and the actual execution of the method will occur in a task that has been submitted to a Spring TaskExecutor. In the simplest case, the annotation may be applied to a void-returning method.

@Async
void doSomething() {
    // this will be executed asynchronously
}

Unlike the methods annotated with the @Scheduled annotation, 
these methods can expect arguments, because they will be invoked in
 the "normal" way by callers at runtime rather than from a scheduled task 
being managed by the container. For example, the following is a legitimate 
application of the @Async annotation.

@Async
void doSomething(String s) {
    // this will be executed asynchronously
}

Even methods that return a value can be invoked asynchronously. 
However, such methods are required to have a Future typed return value. 
This still provides the benefit of asynchronous execution so that the caller
 can perform other tasks prior to calling get() on that Future.

@Async
Future<String> returnSomething(int i) {
    // this will be executed asynchronously
}

@Async methods may not only declare a regular java.util.concurrent.Future return type but also Spring’s org.springframework.util.concurrent.ListenableFuture or, as of Spring 4.2, JDK 8’s java.util.concurrent.CompletableFuture: for richer interaction with the asynchronous task and for immediate composition with further processing steps.

@Async can not be used in conjunction with lifecycle callbacks such as @PostConstruct. To asynchronously initialize Spring beans you currently have to use a separate initializing Spring bean that invokes the @Async annotated method on the target then.

public class SampleBeanImpl implements SampleBean {
    @Async
    void doSomething() {
        // ...
    }
}

public class SampleBeanInitializer {
    private final SampleBean bean;
    public SampleBeanInitializer(SampleBean bean) {
        this.bean = bean;
    }

    @PostConstruct
    public void initialize
        bean.doSomething();
    }
}

The task namespace

Beginning with Spring 3.0, there is an XML namespace for configuring TaskExecutor and TaskScheduler instances. It also provides a convenient way to configure tasks to be scheduled with a trigger.

The 'scheduler' element

The following element will create a ThreadPoolTaskScheduler instance with the specified thread pool size.

<task:scheduler id="scheduler" pool-size="10"/>

The value provided for the 'id' attribute will be used as the prefix for thread names within the pool. The 'scheduler' element is relatively straightforward.If you do not provide a 'pool-size' attribute, the default thread pool will only have a single thread.There are no other configuration options for the scheduler.

The 'executor' element

<task:executor
        id="executorWithCallerRunsPolicy"
        pool-size="5-25"
        queue-capacity="100"
        keep-alive="120"
        rejection-policy="CALLER_RUNS"/>

'scheduled-tasks' element

Basically a "ref" attribute can point to any Spring-managed object, and the "method" attribute provides the name of a method to be invoked on that object. Here is a simple example.

<!--定時(shí)任務(wù)-->
    <bean id="scheduleA" class="world.zw.test.taskSpring.TScheduleA">
        <constructor-arg value="AAA"/>
    </bean>
    <bean id="scheduleB" class="world.zw.test.taskSpring.TScheduleA">
        <constructor-arg value="BBB"/>
    </bean>
    <bean id="scheduleC" class="world.zw.test.taskSpring.TScheduleA">
        <constructor-arg value="CCC"/>
    </bean>
    <task:scheduler id="myScheduler" pool-size="10"/>
    <task:scheduled-tasks scheduler="myScheduler">
        <task:scheduled ref="scheduleA" method="fun" fixed-delay="5000" initial-delay="1000"/>
        <task:scheduled ref="scheduleB" method="fun" fixed-rate="5000"/>
        <task:scheduled ref="scheduleC" method="fun" cron="*/5 * * * * MON-FRI"/>
    </task:scheduled-tasks>

或者都通過注解

@Configuration
@EnableScheduling
public class TScheduleB {
    private String name = "BBB";

    @Scheduled(cron="*/5 * * * * *")
    public void fun(){

        System.out.println("before " + name);
        try {
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("after idMe = " + name);
    }
}

Using the Quartz Scheduler

Quartz uses Trigger, Job and JobDetail objects to realize scheduling of all kinds of jobs. For the basic concepts behind Quartz, have a look at http://quartz-scheduler.org. For convenience purposes, Spring offers a couple of classes that simplify the usage of Quartz within Spring-based applications.

Using the JobDetailFactoryBean

Quartz JobDetail objects contain all information needed to run a job. Spring provides a JobDetailFactoryBean which provides bean-style properties for XML configuration purposes. Let’s have a look at an example:

Cron

想了解Cron最好的方法是看Quartz的官方文檔冈钦。本節(jié)也會(huì)大致介紹一下。

Cron表達(dá)式由6~7項(xiàng)組成李请,中間用空格分開瞧筛。從左到右依次是:秒、分导盅、時(shí)较幌、日、月白翻、周幾乍炉、年(可省略)。值可以是數(shù)字滤馍,也可以是以下符號(hào):
*:所有值都匹配
?:無(wú)所謂岛琼,不關(guān)心,通常放在“周幾”里
,:或者
/:增量值
-:區(qū)間

下面舉幾個(gè)例子巢株,看了就知道了:
0 * * * * *:每分鐘(當(dāng)秒為0的時(shí)候)
0 0 * * * *:每小時(shí)(當(dāng)秒和分都為0的時(shí)候)
*/10 * * * * *:每10秒
0 5/15 * * * *:每小時(shí)的5分槐瑞、20分、35分阁苞、50分
0 0 9,13 * * *:每天的9點(diǎn)和13點(diǎn)
0 0 8-10 * * *:每天的8點(diǎn)困檩、9點(diǎn)、10點(diǎn)
0 0/30 8-10 * * *:每天的8點(diǎn)那槽、8點(diǎn)半窗看、9點(diǎn)、9點(diǎn)半倦炒、10點(diǎn)
0 0 9-17 * * MON-FRI:每周一到周五的9點(diǎn)显沈、10點(diǎn)…直到17點(diǎn)(含)
0 0 0 25 12 ?:每年12約25日圣誕節(jié)的0點(diǎn)0分0秒(午夜)
0 30 10 * * ? 2016:2016年每天的10點(diǎn)半

其中的?在用法上其實(shí)和*是相同的。但是*語(yǔ)義上表示全匹配逢唤,而?并不代表全匹配拉讯,而是不關(guān)心。比如對(duì)于0 0 0 5 8 ? 2016來(lái)說(shuō)鳖藕,2016年8月5日是周五魔慷,?表示我不關(guān)心它是周幾。而0 0 0 5 8 * 2016中的*表示周一也行著恩,周二也行……語(yǔ)義上和2016年8月5日沖突了院尔,你說(shuō)誰(shuí)優(yōu)先生效呢蜻展。

Ref:
https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#scheduling

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市邀摆,隨后出現(xiàn)的幾起案子纵顾,更是在濱河造成了極大的恐慌,老刑警劉巖栋盹,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件施逾,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡例获,警方通過查閱死者的電腦和手機(jī)汉额,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)榨汤,“玉大人蠕搜,你說(shuō)我怎么就攤上這事∈蘸荆” “怎么了讥脐?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)啼器。 經(jīng)常有香客問我旬渠,道長(zhǎng),這世上最難降的妖魔是什么端壳? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任告丢,我火速辦了婚禮,結(jié)果婚禮上损谦,老公的妹妹穿的比我還像新娘岖免。我一直安慰自己,他們只是感情好照捡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布况凉。 她就那樣靜靜地躺著谴返,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上娘赴,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天互躬,我揣著相機(jī)與錄音并思,去河邊找鬼档冬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛薪夕,可吹牛的內(nèi)容都是我干的脚草。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼原献,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼馏慨!你這毒婦竟也來(lái)了埂淮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤写隶,失蹤者是張志新(化名)和其女友劉穎倔撞,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體樟澜,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年叮盘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秩贰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡柔吼,死狀恐怖毒费,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情愈魏,我是刑警寧澤觅玻,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站培漏,受9級(jí)特大地震影響溪厘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜牌柄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一畸悬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧珊佣,春花似錦蹋宦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至惑艇,卻和暖如春蒿辙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背滨巴。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工须板, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人兢卵。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓习瑰,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親秽荤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子甜奄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354