[Spring] @Async와 completablefuture란? :: 잡다한 프로그래밍
반응형

1. @Async란?

spring에서 제공하는 thread를 의미한다.

 

기존 JAVA Thread 사용방법

 

1) Thread 클래스를 상속받는 방법

class CustomThread extends Thread {
	public void run() {
		try {
			for (int i=0; i<100; ++i) {
				System.out.println("[" + getName() + "] CustomThread " + i);
				Thread.sleep(10);
			}			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 

2) Runnable 인터페이스를 상속받아 사용하는방법

class CustomRunnable implements Runnable {
	public void run() {
		try {
			for (int i=0; i<100; ++i) {
				System.out.println("[" + Thread.currentThread().getName() + "] CustomRunnable " + i);
				Thread.sleep(10);
			}			
		} catch (Exception e) {
			e.printStackTrace();
		}		
	}
}

public class ThreadPractice {
	public static void main(String[] args) {
		Thread t1 = new CustomThread();
		Thread t2 = new Thread(new CustomRunnable());
		
		t1.start();
		t2.start();
	}
}

 

@Async 이용하기

@Service
public class TestService {

	@Async
	public void customThread(int i) {
		for (int i=0; i<100; ++i) {
				System.out.println("[" + Thread.currentThread().getName() + "] CustomRunnable " + i);
				Thread.sleep(10);
			}	
	}
}

 

@Congiguration 사용하기

@Configuration
@EnableAsync
public class AsyncConfig {

	@Bean
	public Executor asyncThreadTaskExecutor() {
		ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
		threadPoolTaskExecutor.setCorePoolSize(50);
		threadPoolTaskExecutor.setMaxPoolSize(100);
		threadPoolTaskExecutor.setQueueCapacity(200);
		threadPoolTaskExecutor.setThreadNamePrefix("thread-pool");
		return threadPoolTaskExecutor;
	}

}

 

스프링을 이용하면 스레드 구성을 다음과같이 간편하게 할 수 있다

 

@Asnyc 메소드를 이용할 때 주의사항

  • public 메소드를 이용해야한다
  • 자가호출(self)을 하면 안된다

이러한 주의사항이 있는 이유는 spring AOP가 프록시 패턴의 개념을 이용하기 때문이다. 즉 프록시 클래스에서 @Async 메소드를 호출해야 하는데 private하거나, self로 호출하면 프록시클래스에 의해 호출되지 않기 때문이다.

※프록시 패턴글에 자세한 설명 있음


2. completablefuture란?

@Async같은 비동기 메소드를 사용할 때, Void형태를 사용한다면 문제가 되지 않는다. 하지만 return이 존재한다면 기존에는 Future나 ListenableFuture를 이용하여 해결했지만 JAVA 8버전 부터는 CompletableFuture를 제공하므로 이를 사용하는 방법을 정리한다.

 

다음은 Completeablefuture를 사용한 예제이다.

 

다음과 같이 @Async 메소드에서는 결과 값을 CompletableFuture객체에 담아서 반환해야하고 호출한 곳에서는  이 CompletableFuture 객체를 이용해야한다.

@Service
public class GitHubLookupService {
    
    @Async
    public CompletableFuture findUser(String user) throws InterruptedException {
	//로직
	return CompletableFuture.completedFuture(results);
    }
}

 

return을 CompletableFuture를 통해서 하는것을 알았으니 return 받은 쪽 에서는 어떻게 사용하는지 확인해보자.

 

get 함수란?

다음과 같이 .get()메소드를 통해 결과를 확인할 수 있다.

get을 하게 되면 page1의 수행이 끝날때까지 잠시 기다렸다가 결과값을 반환하게 된다.

public void run(String... args) throws Exception {
    //호출 해서 저장
    CompletableFuture page1 = gitHubLookupService.findUser("PivotalSoftware");
    
    //다른 로직
    System.out.println(page1.get());
}

 

그림으로 표현해보면 다음과 같다.

 

thenCompose 란?

하나의 CompletableFuture가 수행하고 나온 결과를 넘겨주어 다음 CompletableFuture객체가 이를 가지고 다른 메소드를 수행하는 방법.

public void run(String... args) throws Exception {
    //호출 해서 저장
    CompletableFuture<Integer> price1 = testService.getPrice(100);
    
    //다른 로직
    price1.thenCompose(result -> testService.getPrice(result)).join();
}

그림으로 표현하면 다음과 같다.

 

thenCombine 이란?

CompletableFuture를 2개 병렬 실행해서 결과를 조합할 때 사용한다. thenCompose와 달리 두개를 동시에 실행한다는 차이가 있다.

public void run(String... args) throws Exception {
    //호출 해서 저장
    CompletableFuture<Integer> price1 = testService.getPrice();
    CompletableFuture<Integer> price2 = testService.getPrice();
    
    //다른 로직
    price1.thenCombine(price2, (a, b) -> a+b);
}

 

allof 란?

thenCombine처럼 병렬 실행해서 결과를 조합할 때 사용한다. CompletableFuture를 3개이상 조합할 때 사용한다.

public void run(String... args) throws Exception {
    //호출 해서 저장
    CompletableFuture<Integer> price1 = testService.getPrice();
    CompletableFuture<Integer> price2 = testService.getPrice();
    CompletableFuture<Integer> price3 = testService.getPrice();

    //다른 로직
    CompletableFuture.allOf(price1,price2,price3).join();
}

 

allof를 지정하여 사용하고싶지않을 때

리스트에 CompletableFuture객체를 add하고 add한 리스트를 allof에 넣어서 사용할 수 있다.

List<CompletableFuture<Test>> completableFutures = new ArrayList<>();
		for (int j = 0; j <= 23; j++) {
          completableFutures.add(Dao.getCount());
		}
    
List<Test> combinedFuture = CompletableFuture
				.allOf(completableFutures.toArray(new CompletableFuture[completableFutures.size()]))
				.thenApply(result -> completableFutures.stream().map(future -> future.join()).collect(Collectors.toList()))
				.join();
반응형

+ Recent posts