Spring Document : http://static.springsource.org/spring/docs/current/spring-framework-reference/html/scheduling.html#scheduling-task-executor
참고 : http://dev.anyframejava.org/docs/anyframe/plugin/scheduling/4.5.3/reference/html/
https://blog.outsider.ne.kr/1066
Spring Thread Sample : http://www.mkyong.com/spring/spring-and-java-thread-example/
1. 스케줄 (scheduling)
1. 스프링 설정 XML에 추가
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-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">
<!-- <task:annotation-driven scheduler="scheduler"/> <task:scheduler id="scheduler" pool-size="10"/> --> <!-- <task:scheduler id="scheduler" pool-size="10" /> <task:executor id="taskExecutor" pool-size="10"/> <task:annotation-driven executor="taskExecutor" scheduler="scheduler"/>
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="1"/>
<property name="maxPoolSize" value="5"/>
<property name="queueCapacity" value="100"/>
<property name="threadNamePrefix" value="executor-task-"/>
<property name="threadGroupName" value="executor-tasks"/>
</bean>
<bean id="taskScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
<property name="poolSize" value="5"/>
<property name="threadNamePrefix" value="scheduled-task-"/>
<property name="threadGroupName" value="scheduled-tasks"/>
</bean> -->
<!-- pool-size="100-1000" : 초기 100개가 생기고 최대 1000개까지 가능 queue-capacity="1000" : 큐 쌓이는 최대 개수 1000 rejection-policy="ABORT" : 큐 이상 요청이 생겼을 경우 에러 발생 후 무효 처리 ABORT : AbortPolicy 적용. rejection-policy가 정의되지 않았을 경우 기본 적용되는 Policy로 Exception을 throw한다. DISCARD : DiscardPolicy 적용. 모든 Task가 반드시 실행되어야 한다라는 제약점이 없는 경우 적용 가능한 Policy로써 해당 Application이 과부하 상태일 경우 현재 Task의 실행을 Skip한다. DISCARD_OLDEST : DiscardOldestPolicy 적용. 모든 Task가 반드시 실행되어야 한다라는 제약점이 없는 경우 적용 가능한 Policy로써 해당 Application이 과부하 상태일 경우 Queue의 Head에 있는 Task의 실행을 Skip한다. CALLER_RUNS : CallerRunsPolicy 적용. 해당 Application이 과부하 상태일 경우 TaskExecutor에 의해서가 아닌 Thread에서 직접 Task를 실행시킬 수 있게 한다. --> <task:executor id="asyncExecutor" pool-size="5-10" queue-capacity="1000" rejection-policy="ABORT" /> <task:annotation-driven executor="asyncExecutor" />
<!-- 참고 (LinkedBlockingQueue / SynchronousQueue) : http://tyboss.tistory.com/entry/Java-ThreadPoolExecutor-%EC%9D%98-BlockingQueue-SynchronousQueue
아래처럼 taskExecutor를 여러개 등록하여 사용이 가능하다. corePoolSize : Thread 개수 maxPoolSize : Integer.MAX_VALUE (기본값으로 설정) queueCapacity : Integer.MAX_VALUE (기본값으로 설정) keepAliveSeconds : thread 재사용 시간, 60 (초) allowCoreThreadTimeOut : false (메모리는 소모를 더 하지만 퍼포먼스를 높임, Consume memory but high performance), true (메모리는 세이브 할 수 있지만 퍼포먼스가 감소, Could save memory compromising performance)
corePoolSize 만큼 기본 thread가 생성된다. corePoolSize를 1, maxPoolSize를 3, queueCapacity를 1로 설정한 경우 1 request에 3 thread를 처리할 때 첫번째 thread는 바로 실행 2번째 thread는 queue에 쌓이고 3번째 thread 실행이 필요할 때 queue가 꽉 찼기 때문에 queue에 쌓였던 2번째 thread를 실행시키기 위해 maxPoolSize 한도내에서 thread를 새로 생성하여 실행하고 3번째 thread 실행을 queue에 쌓는다. 그래서 2개의 thread가 거의 동시에 실행되고 2 thread 중 1개가 끝나는 순간 queue에 쌓여있던 3번째 thread 요청이 실행된다. 위와 같은 설정일 때 4개의 thread는 실행은 되지만 (maxPoolSize(3) + queueCapacity(1) = 4) 5개의 thread 실행은 4번째 thread가 실행될 수 없으므로 reject policy 정책에 따라 오류를 발생한다. 오류가 발생하더라도 queue에 들어간 요청까지는 처리되므로 총 4개의 thread 요청건이 처리된다. thread가 꽉 차서 RejectedExecutionException 오류가 발생한 경우 keepAliveSeconds 시간이 지나면 thread를 재사용 가능하다. 보통은 corePoolSize만 설정하는데 keppAliveSeconds 를 감안하여 1분동안 처리할 수 있는 thread 개수를 설정한다.
<task:annotation-driven /> 태그는 executor를 사용하지 않아도 되며 annotation-driven 태그는 @Async 등 scan 시 필요하므로 반드시 필요하다. @Async("taskExecutor ID명") 처럼 해당 Async 메소드 실행 시 taskExecutor 를 지정할 수 있다.
annotation-driven에 executor를 지정하면 @Async에 executor ID를 별도로 지정하지 않으면 기본 executor로 등록된다. annotation-driven에 executor를 지정하지 않고 @Async에도 executor ID를 지정하지 않으면 org.springframework.core.task.SimpleAsyncTaskExecutor 로 기본 설정되므로 어떤 스레드도 재사용하지 않고 무한대로 생성되도록 설정되어 있다. --> <bean id="taskExecutor1" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <property name="corePoolSize" value="2" /> </bean> <bean id="taskExecutor2" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <property name="corePoolSize" value="100" /> </bean> <task:annotation-driven executor="taskExecutor1" />
<!-- 실행기 정의에서 pool-size를 100-1000으로 정의 했으므로 실행하는 스레드 풀은 최소 100개에서 최대 1000개를 유지한다. 그리고 이 풀에 실행내용을 전달하기 전에 저장하는 큐의 크기는 queue-capacity로 1000개로 정의했다. 마지막 정의는 큐에도 가득차고 스레드도 가득찬 경우 실행방식을 정의한다. 여기서는 ABORT로 정의 했으므로 예외를 발생시키고 해당 실행을 무효화 시킨다.
이 정의에 의해서 스레드가 동작하는 방식은 빈이 초기화될때 최소 100개의 스레드를 생성한다. 그리고 큐를 통해서 전달된 명령을 처리한다. 만약 큐가 1000개의 명령으로 가득차면 순차적으로 스레드를 증가시킨다. 그리고 스레드, 큐 모두 가득찬 경우에는 마지막 rejection-policy에 따라서 오류 처리를 진행한다. -->
<task:executor id="asyncExecutor" pool-size="100-1000" queue-capacity="1000" rejection-policy="ABORT" /> <task:annotation-driven executor="asyncExecutor" />
</beans:beans> |
1-1. @Configuration 설정 시
참고 : http://docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/scheduling/annotation/EnableAsync.html
http://docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/scheduling/annotation/EnableScheduling.html
http://www.baeldung.com/spring-async
@EnableAsync, @EnableScheduling 은 3.1 이상부터 적용 가능
@Configuration @EnableAsync
public class SpringAsyncConfig {
@Bean(name="taskExecutor") // @Async(value = "taskExecutor") 처럼 사용 public Executor threadPoolTaskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 실행 할 pool size를 지정한다. 기본값 1, 기본적으로 설정한 pool size 대로 thread가 실행되고, pool size 보다 더 많은 요청이 오는 경우 queue capacity 에 쌓이고 queue capacity에 설정한 값보다 넘는 대기열이 있는 경우에 max pool size에 설정한 값 안에서 thread를 추가로 생성하게 된다. // pool size가 처음에는 0 인데 10으로 설정한 경우 core pool size가 10이 초과되면 keepAliveSeconds 시간 이후에 thread를 kill 하는데 core pool size 이하로는 kill을 하지 않는다. pool size 가 10 이상이 된 이후로는 최소 10 pool size를 계속 유지한다. executor.setCorePoolSize(10);
// 최대 pool size 지정, 기본값 Integer.MAX, 기본적으로는 core pool size 안에서 해결하고 core pool size + queue capacity 를 넘는 요청이 왔을 때만 max pool size 안에서 pool를 생성한다. executor.setMaxPoolSize(100);
// 대기열 size 지정, 기본값 Integer.MAX, 0으로 설정한 경우 queue type이 SynchronousQueue를, 0 초과로 설정한 경우 LinkedBlockingQueue 를 생성한다. 해당 값을 0으로 설정하지 않으면 pool size 가 core pool size 이상 생성되지 않는다 (대기열만 Integer.MAX). executor.setQueueCapacity(0);
// core thread 를 유지할 timeout (초) 지정, 기본값 60(초), setAllowCoreThreadTimeOut 이 true 일 경우에만 작동한다. executor.setAllowCoreThreadTimeout(60);
// core thread 를 유휴시간 (keepAliveSeconds)이 지나서 kill 할 지 여부, 기본값 false, true로 설정한 경우 core pool 도 keepAliveSeconds 시간 이후에 thread 를 kill한다. jdk 1.6 이상부터 사용가능. executor.setAllowCoreThreadTimeOut(true);
// Prefix thread 명 지정 executor.setThreadNamePrefix("[Async Executor]");
// queue 대기열 및 task 가 완료된 이후에 shutdown 여부 executor.setWaitForTasksToCompleteOnShutdown(true);
// executor 초기화 // executor.initialize();
return executor; }
}
|
2. 스케줄 Service 등록
package iwa.test.service; import java.util.Date; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @Service public class Test3Service { @Scheduled(fixedDelay=5000) public void printWithFixedDelay() { System.out.println("execute printWithFixedDelay() of Annotated PrintTask at " + new Date()); } @Scheduled(fixedRate = 10000) public void printWithFixedRate() { System.out.println("execute printWithFixedRate() of Annotated PrintTask at " + new Date()); } @Scheduled(cron = "*/8 * * * * MON-FRI") public void printWithCron() { System.out.println("execute printWithCron() of Annotated PrintTask at " + new Date()); } } |
cron | Cron Expression을 이용하여 Task 실행 주기 정의.
Cron Expression은 6개의 Field로 구성되며 각 Field는 순서대로 second, minute, hour, day, month, weekday를 의미한다. 각 Field의 구분은 Space로 한다. 또한 month와 weekday는 영어로 된 단어의 처음 3개의 문자로 정의할 수 있다. 0 0 * * * * : 매일 매시 시작 시점 */10 * * * * * : 10초 간격 0 0 8-10 * * * : 매일 8,9,10시 0 0/30 8-10 * * * : 매일 8:00, 8:30, 9:00, 9:30, 10:00 0 0 9-17 * * MON-FRI : 주중 9시부터 17시까지 0 0 0 25 12 ? : 매년 크리스마스 자정
* org.springframework.scheduling.support.CronSequenceGenerator API 참조 |
fixed-delay | 이전에 실행된 Task의 종료 시간으로부터의 fixed-delay로 정의한 시간만큼 소비한 이후 Task 실행. (Milliseconds 단위로 정의) |
fixed-rate | 이전에 실행된 Task의 시작 시간으로부터 fixed-rate로 정의한 시간만큼 소비한 이후 Task 실행. (Milliseconds 단위로 정의) |
필드이름 값 seconds 0~59 minutes 0~59 hours 0~23 day of month 1~31 month 1~12 day of week 1~7(1-일,7-토/MON,SUN...) years (optional) 1970~2099 특수 문자 설명 * : 모든 수를 의미. minutes위치에 있으면 매분마다 라는 뜻. ? : day of month, day of week에만 사용 가능. 특별한 값이 없다는 뜻. - : 기간을 설정. hours 위치에 10-12 라고 쓰면 10, 11, 12에 동작하라는 뜻. , : 특정 시간을 설정. day of week위치에 2,4,6 라고 쓰면, 월,수,금에만 동작하란 뜻. / : 증가를 표현. seconds위치에 0/15로 설정되어 있으면, 0초에 시작해서 15초 간격으로 동작하라는 뜻. L : day of month, day of week에만 사용하며, 마지막 날의 의미 day of month에 L로 되어 있으면 그 달의 마지막 날에 실행하라는 뜻. W : day of month에만 사용하며, 가장 가까운 평일을 의미. 15W로 설정되어 있고 15일이 토요일이면, 가장 가까운 평일인 14일 금요일에 실행, 15일이 일요일이면 16일 월요일에 실행된다. 15일이 평일이면 그날 그대로 실행됨. LW : L과 W를 결합하여 사용. 그달의 마지막 평일의 의미 # : day of week에 사용, 6#3 3번째 주 금요일이란 의미
Expression Meaning "0 0 12 * * *" 매일 12시에 실행 "0 15 10 * * *" 매일 10시 15분에 실행 "0 * 14 * * *" 매일 14시에 시작해서 "0 0/5 14 * * *" 매일 14시에 시작해서 5분간격으로 실행 "0 0/5 14,18 * * *" 매일 14시, 18시에 시작해서 5분간격으로 실행 "0 0-5 14 * * *" 매일 14시에 시작해서 0분동안 실행
fixed-delay - 이전에 실행된 태스크의 종료시간으로 부터 정의된 시간만큼 지난 후 태스크를 실행 (밀리세컨드 단위) fixed-rate - 이전에 실해된 태스크의 시작시간으로 부터 정의된 시간만큼 지난 후 태스크 |
XML에 정의하여 사용하는 방법
<task:scheduled-tasks> <task:scheduled ref="sampleScheduler" method="schedule" cron="0/15 * * * * *" /> </task:scheduled-tasks> |
@Scheduled 사용 시 cron 설정값을 properties에서 가져오는 방법
<util:properties id="baseConfig" location="classpath:conf/base-config.xml" /> <util:properties id="appConfig" location="classpath:conf/${spring.profiles.active}-config.xml" /> <context:property-placeholder properties-ref="appConfig" />
@Scheduled(cron = "${cron.time}")
|
* Thread 개수 확인
@Autowired private ThreadPoolTaskExecutor threadPoolTaskExecutor; @RequestMapping("threadMonitor.json") public void threadCount(Box paramBox, ModelBox modelBox) { LOG.debug("getCorePoolSize : {}", threadPoolTaskExecutor.getCorePoolSize()); LOG.debug("getMaxPoolSize : {}", threadPoolTaskExecutor.getMaxPoolSize()); LOG.debug("getActiveCount : {}", threadPoolTaskExecutor.getActiveCount()); LOG.debug("getKeepAliveSeconds : {}", threadPoolTaskExecutor.getKeepAliveSeconds()); LOG.debug("getPoolSize : {}", threadPoolTaskExecutor.getPoolSize()); // 현재 pool size 확인 LOG.debug("getThreadGroup : {}", threadPoolTaskExecutor.getThreadGroup()); LOG.debug("getThreadNamePrefix : {}", threadPoolTaskExecutor.getThreadNamePrefix()); LOG.debug("getThreadPriority : {}", threadPoolTaskExecutor.getThreadPriority()); } |
----------------------------------------------------------------------------------------------------------------------------------------------------
2. @Async
참고 : http://yasol.tistory.com/entry/Spring-Async-pattern-%EC%98%88%EC%A0%9C
1. Service 정의
package iwa.test.service; import java.util.Date; import java.util.concurrent.Future; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Service; @Service public class Test3Service { @Async public void printWithAsync() throws Exception { System.out.println("execute printWithAsync() of AsyncPrintTask at Start : " + new Date()); Thread.sleep(5000); System.out.println("execute printWithAsync() of AsyncPrintTask at End : " + new Date()); }
@Async public void printWithArg(int i) throws Exception { System.out.println("execute printWithArg(" + i + ") of AsyncPrintTask at Start : " + new Date()); Thread.sleep(5000); System.out.println("execute printWithArg(" + i + ") of AsyncPrintTask at End : " + new Date()); }
// 응답이 필요한 경우 Future 객체를 사용한다. @Async public Future<String> returnVal(int i) throws Exception { System.out.println("execute returnVal() of AsyncPrintTask"); Date current = new Date(); Thread.sleep(5000); System.out.println(new Date()); return new AsyncResult<String>(current.toString()); } } |
2. Controller 에서 Service Call
@RequestMapping(value = "/test2.do") public String test2(Model model) throws Exception { test3Service.printWithAsync(); test3Service.printWithArg(100); Future<String> recvMessage = test3Service.returnVal(20); // System.out.println("recvMessage : " + recvMessage); // recvMessage.get() 처럼 get() 으로 AsyncResult 값을 리턴 받으면 작업은 Async로 처리하지만 Contoller에서는 값을 받을 때까지 기다린다. System.out.println("recvMessage : " + recvMessage.get());
/*
while ( true ) {
if ( recvMessage.isDone()) {
System.out.println( "Result from asynchronous process - " + recvMessage.get());
break ;
}
System.out.println( "Continue doing something else. " );
Thread.sleep( 1000 );
} */ return "test/test2"; } |
=============================================================================================================
@Async, @Scheduling <task:annotation-driven /> 설정을 @Configuration 설정 시 Override
@Configuration
@EnableScheduling
@EnableAsync(
mode = AdviceMode.PROXY, proxyTargetClass = false,
order = Ordered.HIGHEST_PRECEDENCE
)
@ComponentScan(
basePackages = "hello"
)
public class RootContextConfiguration implements
AsyncConfigurer, SchedulingConfigurer {
@Bean
public ThreadPoolTaskScheduler taskScheduler()
{
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(20);
scheduler.setThreadNamePrefix("task-");
scheduler.setAwaitTerminationSeconds(60);
scheduler.setWaitForTasksToCompleteOnShutdown(true);
return scheduler;
}
@Override
public Executor getAsyncExecutor()
{
Executor executor = this.taskScheduler();
return executor;
}
@Override
public void configureTasks(ScheduledTaskRegistrar registrar)
{
TaskScheduler scheduler = this.taskScheduler();
registrar.setTaskScheduler(scheduler);
}
}
|
=============================================================================================================
출처 : http://www.javabeat.net/task-execution-scheduling-spring/
pring 2.0 introduced abstractions for asynchronous execution and scheduling of tasks. The key interfaces for scheduling and task execution are as listed as TaskExecutor, TaskScheduler, Trigger, TriggerContext and ScheduledFuture. Let take a look at each of these interfaces. This article explores spring’s scheduler related APIs in detail. You can read the official explanation for this API’s here.
follow us on @twitter and @facebook
TaskExecutor
TaskExecutor was introduced as an abstraction for dealing with executors. Executors are the Java 5 name for the concept of thread pools. The primary aim of TaskExecutor is to abstract away the need for Java 5 when using thread pools. The interface has only a single method as shown below:
public interface TaskExecutor extends java.util.concurrent.Executor {
void execute(Runnable task);
}
|
TaskExecutor Implementation Classes
- SimpleAsyncTaskExecutor: Does not reuse threads. It starts a new thread and is asynchronus.
- SyncTaskExecutor: No thread reuse and is synchonous. Instead, each invocation takes place in the calling thread.
- ConcurrentTaskExecutor: Exposes the Java 5 java.util.concurrent.Executor.
- SimpleThreadPoolTaskExecutor: It is a subclass of Quartz’s SimpleThreadPool which listens to Spring’s lifecycle callbacks.
- ThreadPoolTaskExecutor: It exposes bean properties for configuring a ThreadPoolExecutor configuration and and wraps it in a TaskExecutor.
- TimerTaskExecutor: This implementation uses a single TimerTask as its backing implementation. It’s different from the SyncTaskExecutor in that the method invocations are executed in a separate thread, although they are synchronous in that thread.
- WorkManagerTaskExecutor: This implementation uses the CommonJ WorkManager as its backing implementation
TaskScheduler
This interface has a variety of methods for scheduling tasks to run at some point in the future, as seen below:
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);
} |
FixedRate – period will be measured from the start time
FixedDelay – period will be measured from the completion time
TaskScheduler Implementation Classes
- TimerManagerTaskScheduler: Delegates to a CommonJ TimerManager instance, typically configured with a JNDI-lookup.
- ThreadPoolTaskScheduler: Used whenever external thread management is not a requirement. Internally, it delegates to a ScheduledExecutorService instance.
Trigger
Using Trigger the execution times may be determined based on past execution outcomes or even arbitrary conditions. The Trigger interface is as follows:
public interface Trigger {
Date nextExecutionTime(TriggerContext triggerContext);
} |
TriggerContext is an interface and encapsulates all of the relevant data.
Trigger Implementation Classes
- CronTrigger: It enables the scheduling of tasks based on cron expressions.
- PeriodicTrigger: It accepts a fixed period, an optional initial delay value, and a boolean to indicate whether the period should be interpreted as a fixed-rate or a fixed-delay.
Bootstrapping the scheduler
Since Spring 3.0, XML namespace for configuring TaskExecutor and TaskScheduler instances is available. It also provides a convenient way to configure tasks to be scheduled with a trigger. Add the following schema to the spring configuration file(xml file):
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd |
Task Namespace
Add the following configuration :
<task:executor id="executor" pool-size="8-25" queue-capacity="100" />
<task:scheduler id="scheduler" pool-size="10" /> |
- task:executor – Create instance of ThreadPoolTaskExecutor
- task:scheduler – Create instance of ThreadPoolTaskScheduler
- pool-size – If the value is not provided, then the default thread pool will only have a single thread.
- queue-capacity – Number of tasks held back. Default value is Unbound
- rejection-policy – This another attribute not mentione the above example. Used for to throw exception when a task is rejected. Its default value is AbortPolicy. Other possbile values are – DiscardPolicy , DiscardOldestPolicy , and CallerRunsPolicy.
Scheduling the Tasks
Tasks can be scheduled in the following three ways:
- Annotation Driven
- XML Driven
- Programmatically
1. Annotation Driven
The @Scheduled annotation can be added to a method along with trigger metadata for task scheduling. The @Async annotation can be provided on a method so that invocation of that method will occur asynchronously.
Add following to your spring configuration file:
<context:component-scan annotation-config="true" base-package="com.javabeat"/>
<task:annotation-driven executor="executor" scheduler="scheduler" /> |
The following example will only execute on weekdays.:
@Scheduled(cron="*/5 * * * * MON-FRI")
public void doSomeTask(){
// some task that should execute on weekdays only
} |
Scheduled methods must have void returns and must not expect any arguments.
Syntax for @Async annotation is:
@Async
void doSomething() {
// this will be executed asynchronously
} |
2. XML Driven
Add the following configuration to the spring configuration file :
<task:scheduled-tasks scheduler="sampleScheduler">
<task:scheduled ref="someObject"
method="someMethod" fixed-delay="5000"/>
</task:scheduled-tasks> |
3. Programmatically
Go for programmatic way when your application desire more control on scheduling. For example :
ThreadPoolTaskScheduler scheduler = (ThreadPoolTaskScheduler) appContext.getBean("scheduler");
CronTrigger trigger = new CronTrigger("*/5 * * * * MON-FRI");
ScheduledFuture<Object> scedulefuture= scheduler.schedule(taskObject, trigger );
|
In the above example instead of schedule() method, you can use scheduleAtFixedRate(.. , ..) or scheduleWithFixedDelay(.. , ..).
==========================================================================================================
POJO (Plain Old Java Object) 로 구현된 Thread 실행
- SampleThreadPrintService.java
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.olleh.lupin.app.base.util.ApiUtil; import com.olleh.lupin.cmn.data.Box; public class SampleThreadPrintService implements Runnable { private static final Logger LOG = LoggerFactory.getLogger(SampleThreadPrintService.class); private String name; private Box paramBox; public SampleThreadPrintService(String name, Box paramBox) { this.name = name; this.paramBox = paramBox; } @Override public void run() { ApiUtil.setThreadName(paramBox); LOG.debug(name + " is running"); try { // Thread.sleep(new Random().nextInt(5000)); Thread.sleep(3000); } catch (Exception e) { e.printStackTrace(); } LOG.debug(name + " is end"); } } |
- Controller
@Autowired @Qualifier("taskExecutor1") private ThreadPoolTaskExecutor taskExecutor;
@RequestMapping("print.json") public void print(Box paramBox, ModelBox modelBox) { taskExecutor.execute(new SampleThreadPrintService("Thread 1", paramBox)); taskExecutor.execute(new SampleThreadPrintService("Thread 2", paramBox)); taskExecutor.execute(new SampleThreadPrintService("Thread 3", paramBox)); } |
※ @Async 메소드는 static 을 사용할 수 없다.
spring 에서 관리하므로 호출도 spring bean을 이용해 호출하여야 한다.
내부 메소드를 바로 호출할 경우 self autowired 를 이용하여 내부 메소드를 호출한다.
Self autowired 방법
@Service public class MsgUtil {
private static MsgUtil msgUtil;
@Autowired private ApplicationContext applicationContext;
@PostConstruct private void initialize() { MsgUtil.msgUtil = applicationContext.getBean(MsgUtil.class); }
} |