美文网首页
Spring——Job线程池

Spring——Job线程池

作者: jiangmo | 来源:发表于2018-05-31 17:28 被阅读163次
  • Spring框架提供了线程池和定时任务执行的抽象接口:TaskExecutor和TaskScheduler来支持异步执行任务和定时执行任务功能。
  • 同时使用框架自己定义的抽象接口来屏蔽掉底层JDK版本间以及Java EE中的线程池和定时任务处理的差异。 为啥?因为Spring想要替你管理线程池的初始化、注解使用、停止等工作。
  • 另外Spring还支持集成JDK内部的定时器Timer和Quartz Scheduler框架。
  • 这里我们单说TaskExecutor和TaskScheduler

ThreadPoolTaskExecutor

配置

<!-- spring thread pool executor -->           
    <bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <!-- 线程池维护线程的最少数量 -->
        <property name="corePoolSize" value="5" />
        <!-- 允许的空闲时间 -->
        <property name="keepAliveSeconds" value="200" />
        <!-- 线程池维护线程的最大数量 -->
        <property name="maxPoolSize" value="10" />
        <!-- 缓存队列 -->
        <property name="queueCapacity" value="20" />
        <!-- 对拒绝task的处理策略 -->
        <property name="rejectedExecutionHandler">
            <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
        </property>
    </bean>

execute(Runable)方法执行过程 (与JDK的线程池一样机制)

  • 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。

如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。

如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maxPoolSize,建新的线程来处理被添加的任务。

如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maxPoolSize,那么通过handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程 maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

测试使用

@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

首先给出几个结论:

  • 调度器本质上还是通过juc的ScheduledExecutorService进行的
  • 调度器启动后你无法通过修改系统时间达到让它马上执行的效果
  • 被@Schedule注解的方法如果有任何Throwable出现, 不会中断后续Task, 只会打印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.
因为定时任务需要记录任务的前后执行时间点序列,如果后续修改了系统时间,则会破坏既定时间序列,故需要记录最开始预订好的时间序列。

public interface Trigger {
    // 获取下一个执行时间
    Date nextExecutionTime(TriggerContext triggerContext);
}
// 相对时间记录载体
public interface TriggerContext {
    Date lastScheduledExecutionTime();
    Date lastActualExecutionTime();
    Date lastCompletionTime();
}

Trigger implementations

如常用的一个表达式实现:
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.

<!--定时任务-->
    <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的官方文档。本节也会大致介绍一下。

Cron表达式由6~7项组成,中间用空格分开。从左到右依次是:秒、分、时、日、月、周几、年(可省略)。值可以是数字,也可以是以下符号:
*:所有值都匹配
?:无所谓,不关心,通常放在“周几”里
,:或者
/:增量值
-:区间

下面举几个例子,看了就知道了:
0 * * * * *:每分钟(当秒为0的时候)
0 0 * * * *:每小时(当秒和分都为0的时候)
*/10 * * * * *:每10秒
0 5/15 * * * *:每小时的5分、20分、35分、50分
0 0 9,13 * * *:每天的9点和13点
0 0 8-10 * * *:每天的8点、9点、10点
0 0/30 8-10 * * *:每天的8点、8点半、9点、9点半、10点
0 0 9-17 * * MON-FRI:每周一到周五的9点、10点…直到17点(含)
0 0 0 25 12 ?:每年12约25日圣诞节的0点0分0秒(午夜)
0 30 10 * * ? 2016:2016年每天的10点半

其中的?在用法上其实和*是相同的。但是*语义上表示全匹配,而?并不代表全匹配,而是不关心。比如对于0 0 0 5 8 ? 2016来说,2016年8月5日是周五,?表示我不关心它是周几。而0 0 0 5 8 * 2016中的*表示周一也行,周二也行……语义上和2016年8月5日冲突了,你说谁优先生效呢。

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

相关文章

网友评论

      本文标题:Spring——Job线程池

      本文链接:https://www.haomeiwen.com/subject/qxatsftx.html