-
개요
Spring Async annotation를 이용하여 간단하게 비동기 처리가 가능하다.
@async 를 선언한 메소드를 호출한 호출자는 즉시 리턴하고 실제 실행은 Spring TaskExecutor에 의해 실행.
메서드는 Future 타입 값을 리턴하여 해당 Future에 get() 메서드를 이용하여 작업 수행을 할 수 있다.
스프링 TaskExecutor
Executor는 스레드 풀의 개념으로 Java 5에서 도입된 개념. 구현체가 실제 Pool이라고 확신 할 수 없어 Executor(직역: 집행자)라 사용.
TaskExecutor 인터페이스는 실행하는 Task를 받고 execute 메서드를 갖는다.
TaskExecutor 종류
- SimpleAsyncTaskExecutor
- 이 구현에는 어떤 스레드도 재사용하지 않고 호출마다 새로운 스레드를 시작
- 동시접속 제한(concurrency limit)을 지원 제한 수가 넘어서면 빈 공간이 생길 때까지 모든 요청을 block - SyncTaskExecutor
- 호출을 비동기적으로 수행하지 않는다. 대신, 각각의 호출은 호출 쓰레드로 대체된다. 간단한 테스트케이스와 같이 필요하지 않은 멀티쓰레드 상황에서 사용된다. - ConcurrentTaskExecutor
- java.util.concurrent.Executor Wrapper. - SimpleThreadPoolTaskExecutor
- Spring의 생명주기 콜백을 듣는 Quartz의 SimpleThreadPool의 하위클래스.
- Quartz와 Quartz가 아닌 컴포넌트간에 공유될 필요가 있는 쓰레드 풀 - ThreadPoolTaskExecutor
- 자바 5에서 가장 일반적으로 사용.
- java.util.concurrent.ThreadPoolExecutor를 구성하는 bean 프로퍼티를 노출하고 이를 TaskExecutor로 감싼다. - TimerTaskExecutor
- 지원되는 구현물 중 하나의 TimerTask를 사용.
- 쓰레드에서 동기적이더라도 메소드 호출이 개별 쓰레드에서 수행되어 SyncTaskExecutor와 다르다. - WorkManagerTaskExecutor
- 지원되는 구현물 중 하나로 CommonJ WorkManager을 사용
- Spring 컨텍스트에서 CommonJ WorkManager참조를 셋팅하기 위한 중심적이고 편리한 클래스
- SimpleThreadPoolTaskExecutor와 유사하게, 이 클래스는 WorkManager인터페이스를 구현하고 WorkManager만큼 직접 사용
@Async를 활용한 간단한 구현
1. AsyncConfigurer 구현
@Configuration
@EnableAsync
public class Config implements AsyncConfigurer {
private static int TASK_CORE_POOL_SIZE = 2;
private static int TASK_MAX_POOL_SIZE = 4;
private static int TASK_QUEUE_CAPACITY = 10;
private static String BEAN_NAME = "executorSample"; @Resource(name = "executorSample")
private ThreadPoolTaskExecutor executorSample;}
- @Configuration 을 이용한 bean등록
- EnableAsync를 이용하여 @async를 이용하겠다 알린다.
- AsyncConfigurer 구현
- TASK_CORE_POOL_SIZE : 기본 Thread 수
- TASK_MAX_POOL_SIZE : 최대 Thread 수
- TASK_QUEUE_CAPACITY : queue 수
- BEAN_NAME : bean 이름
- 실행할 테스크의 수는 QUEUE_SIZE+MAX_POOL_SIZE 보다 크면 안된다.
- POOL생성 과정
1. 기본 thread(TASK_CORE_POOL_SIZE) 수까지 순차적으로 쌓인다
2. 기본 thread(TASK_CORE_POOL_SIZE) 크기가 넘어 설 경우 queue에 쌓인다
3. 큐에 최대치까지 쌓이면TASK_MAX_POOL_SIZE까지 순차적으로 한개씩 증가시킨다.
2. AsyncConfigurer 의 필수 Override 메서드 구현
@Bean(name = "executorTest")
@Override
public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(TASK_CORE_POOL_SIZE);
executor.setMaxPoolSize(TASK_MAX_POOL_SIZE);
executor.setQueueCapacity(TASK_QUEUE_CAPACITY);
executor.setBeanName(BEAN_NAME);
executor.initialize(); return executor;}@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler();}
- 위에서 설정한 상수들 할당하여 쓰레드, 큐, bean name을 설정한다.
- getAsyncUncaughtExceptionHandler 에 대해 설정 AsyncUncaughtExceptionHandler에 대해 구현해주어야 한다.
- 만약 멀티로 executor를 생성할 경우 @Override대신 @Qualifier를 선언해준다.
3. AsyncTask 생성
@Service("asyncTask")
public class AsyncTask{ @Async("executorTest")
public void executor(String str) {
System.out.println("result:"+str); }
}
- task 클래스 메서드 @Async 어노테이션에 Executor명을 적어준다.
4. 실행
public class AsyncController{
@Resource(name = "asyncTask")
private AsyncTask asyncTask;
@Resource(name = "Config")
private Config config; @RequestMapping("/test.do")
public ModelAndView doTask(HttpServletRequest request, HttpServletResponse response) throws Exception {
asyncTask.executor("TEST"); }}
- 새로운 쓰레드가 생기며, test가 출력되는 것을 확인 할 수 있다.