1. 프로젝트 테스트
- async project, web, lombok
- Package : controller, service
- Class : ApiController, AsyncService
01. 간단한 Async 처리
- Main AsyncApplication.java
- @EnableAsync
- 스프링의 Async 어노테이션을 감지하며 EJB 3.1javax.ejb.Asynchronous; 이 옵션으로 사용자 정의된 다른 어노테이션 또한 감지할 수 있다.
package com.example.async;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
}
}
- controller / ApiController.java
package com.example.async.controller;
import com.example.async.service.AsyncService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api")
public class ApiController {
final private AsyncService asyncService;
@GetMapping("/hello")
public String hello() {
asyncService.hello();
log.info("method end");
return "hello";
}
}
- service / AsyncService.java
- @Async
- 비동기 처리를 위한 어노테이션, spring에서 제공하는 thread
- @Async 어노테이션을 빈bean에 넣으면 별도의 쓰레드에서 실행되도록 동작
- public method에만 적용가능
- @Service
- 서비스 레이어 클래스들에 사용되어지는 어노테이션으로 @Component를 사용해도 상관 없다
- @Service 어노테이션을 사용함으로써 해당 클래스가 서비스 레이어 클래스라는 것을 명확하게 할 수 있다
- @Service 어노테이션은 해당 클래스를 루트 컨테이너에 빈(Bean) 객체로 생성해주는 어노테이션
package com.example.async.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class AsyncService {
@Async
public void hello() {
for(int i = 0; i < 10; i ++) {
try {
Thread.sleep(2000);
log.info("thread sleep");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
![](https://velog.velcdn.com/images%2Fansalstmd%2Fpost%2F94d93532-1eaa-470e-a8d3-7ffdc63e4417%2Fimage.png)
02. CompletableFuture 사용
- CompletableFuture
- @Async같은 비동기 메소드를 사용할 때, Void형태를 사용한다면 문제가 되지 않는다. 하지만 return이 존재한다면 기존에는 Future나 ListenableFuture를 이용하여 해결했지만 JAVA 8버전 부터는 CompletableFuture를 제공
- 단일 api 전송 보다는 여러개의 api를 동시에 전송을 한 다음에 그 결과를 조인해서 보일 때 사용하는 것이 더 효율적이기도 하다
- AsyncService.java
package com.example.async.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Slf4j
@Service
public class AsyncService {
// CompletableFuture
// 단일 api 전송 보다는 여러개의 api를 동시에 전송을 한 다음에
// 그 결과를 조인해서 보일 때 사용하는 것이 더 효율적이기도 하다
@Async
public CompletableFuture run() {
return new AsyncResult(hello()).completable();
}
public String hello() {
for(int i = 0; i < 10; i ++) {
try {
Thread.sleep(2000);
log.info("thread sleep");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "async hello";
}
}
package com.example.async.controller;
import com.example.async.service.AsyncService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.CompletableFuture;
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api")
public class ApiController {
final private AsyncService asyncService;
// CompletableFuture
// @Async같은 비동기 메소드를 사용할 때, Void형태를 사용한다면 문제가 되지 않는다. 하지만 return이 존재한다면 기존에는 Future나 ListenableFuture를 이용하여 해결했지만 JAVA 8버전 부터는 CompletableFuture를 제공
@GetMapping("/hello")
public CompletableFuture hello() {
log.info("completable future init");
return asyncService.run();
}
}
![](https://velog.velcdn.com/images%2Fansalstmd%2Fpost%2Fa063ad06-cabd-4a97-b2ea-1ab6b5204a6f%2Fimage.png)
03. Thread 만들기
- Package : config
- Class : AppConfig
- AppConfig.java
package com.example.async.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
public class AppConfig {
@Bean("async-thread")
public Executor asyncThread() {
ThreadPoolTaskExecutor threadPoolExecutor = new ThreadPoolTaskExecutor();
threadPoolExecutor.setMaxPoolSize(100); // 최대 갯수
threadPoolExecutor.setCorePoolSize(10); // 코어 갯수
threadPoolExecutor.setQueueCapacity(10); // Queue 갯수
threadPoolExecutor.setThreadNamePrefix("Async-");
// 동작은 먼저 Pool의 갯수만큼 쓰레드가 먼저 작동을 하고 가득차면
// Queue의 스레드 만큼 사용하고 또 가득차면 Pool 이렇게 계속 번갈아가면서
// 최대 갯수 만큼 쓰레드가 증가한다
return threadPoolExecutor;
}
}
package com.example.async.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Slf4j
@Service
public class AsyncService {
// CompletableFuture
// 단일 api 전송 보다는 여러개의 api를 동시에 전송을 한 다음에
// 그 결과를 조인해서 보일 때 사용하는 것이 더 효율적이기도 하다
@Async("async-thread")
public CompletableFuture run() {
return new AsyncResult(hello()).completable();
}
public String hello() {
for(int i = 0; i < 10; i ++) {
try {
Thread.sleep(2000);
log.info("thread sleep");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "async hello";
}
}
![](https://velog.velcdn.com/images%2Fansalstmd%2Fpost%2F6fc95a91-ea0b-4ffa-9a21-7363d62523c7%2Fimage.png)
- @Async 어노테이션에 @Bean으로 등록한 thread 호출
![](https://velog.velcdn.com/images%2Fansalstmd%2Fpost%2Fc7933871-0442-49a6-8068-ed28c7f10928%2Fimage.png)