자바 (2)
동기(Synchronous)/비동기(Asynchronous) 처리

다음과 같이 여러개의 작업을 동시에 처리할때 작업1,2가 메인작업과 별개의 작업일때

비동기(@Async)로 던져놓고 메인 작업만 완료 후 응답을 주기도 합니다.

(응답시간 0.5초, 작업1,2의 결과 받을 수 없음)

 

메인작업 : 0.5초

작업1 : 1초

작업2 : 2초

 

하지만 3가지 작업의 결과가 모두 필요할 경우 동기 처리를 하면 3.5초가 걸리지만

작업1, 2를 비동기로 처리하면 일찍 처리가 끝난 작업은 모든 작업이 완료될때까지 대기 후 응답을 줍니다.

(응답시간 2초, 3개의 작업 결과 확인 가능)

 

Kotlin

@RestController
@EnableAsync
class ThreadTest(
        val taskService: TaskService,
        val mainService: MainService
) {
    @GetMapping("task")
    fun testMapping(): String {
        val start = System.currentTimeMillis()

        val task1 = taskService.task1()
        val task2 = taskService.task2()
        val main = mainService.task()

        return "main[$main], task1[${task1.get()}], task2[${task2.get()}], total[${System.currentTimeMillis()-start}]"
    }
}

@Service
class MainService {
    fun task(): Long {
        val start = System.currentTimeMillis()
        Thread.sleep(500)
        return (System.currentTimeMillis()-start)
    }
}

@Service
class TaskService {
    @Async
    fun task1(): CompletableFuture<Long> {
        val start = System.currentTimeMillis()
        Thread.sleep(1000)
        return CompletableFuture.completedFuture(System.currentTimeMillis()-start)
    }

    @Async
    fun task2(): CompletableFuture<Long> {
        val start = System.currentTimeMillis()
        Thread.sleep(2000)
        return CompletableFuture.completedFuture(System.currentTimeMillis()-start)
    }
}

 

테스트1

http://localhost:8080/task

main[502], task1[1001], task2[2001], total[2007]


메인 작업은 처리 완료 후 비동기 작업들이 완료될때까지 대기 후 응답을 주고 있습니다.

 

테스트2

반대로 동기로 처리하는 메인작업이 오래걸릴 경우도 테스트 해보겠습니다.

(MainService의 작업시간을 3초로 늘려주세요)

 

메인작업 : 3초

작업1 : 1초

작업2 : 2초

 

http://localhost:8080/task

main[3001], task1[1001], task2[2001], total[3004]


당연히 메인작업이 처리되는동안 1,2번 작업은 비동기로 작업을 완료하고 응답을 같이 주고 있습니다.

 

 

* 동시에 여러 곳을 연동해서 결과를 취합한다거나

  자원이 남아 별도의 스레드에서 처리하고자 할 때 사용하시면 됩니다.

 

 

 

 

 

 

  Comments,   0  Trackbacks
댓글 쓰기
Threadlocal을 이용하여 사용자별 요청 처리하기





개발시 소스 전체에서 사용할 수 있는 전역변수처럼 데이터를 할당하고 싶을떄가 있습니다.

Threadlocal을 이용하여 이를 구현해 봅니다.


ShoppingController.java

@RestController
public class ShoppingController {

	private int melon;

	@RequestMapping("/")
	private String test() {
		this.addMelon();
		
		return String.valueOf(melon);
	}
	
	private void addMelon() {
		melon++;
	}
}

사용자별로 요청 시 카트에 멜론을 담는 기능을 구현하려고 합니다.  이 떄 카트 수량은 여러 메소드에서 사용할 수 있기 때문에 멤버변수로 선언을 합니다.


이렇게 작성 후 실행을 해보면 카운트가 초기화가 되지 않고 계속 증가하는 것을 볼 수 있습니다.  스프링의 경우 빈 관리를 싱글톤으로 하기 때문에 ShoppingController 객체는 요청 시 재사용 됩니다.  멤버변수 역시 초기화가 되지 않습니다.




아래는 Threadlocal을 이용해 사용자별 카트를 구현한 소스입니다.


ThreadRepository.java

public class ThreadRepository {
	private static ThreadLocal<Cart> threadLocal = new ThreadLocal<Cart>() {
		@Override
		protected Cart initialValue() {
			return new Cart();
		};
	};
	
	public static Cart getCart() {
		return threadLocal.get();
	}
	
	public static void remove() {
		threadLocal.remove();
	}
}

카트를 담을 저장소를 만들어 줍니다.  Thradlocal을 이용하여 Thread별로 저장소가 생성이 됩니다.


Cart.java

public class Cart {
	private int apple = 0;
	private int orange = 0;
	private int melon = 0;
	
	public void addApple() {
		this.apple++;
	}

	public void addOrange() {
		this.orange++;
	}

	public void addMelon() {
		this.melon++;
	}

	@Override
	public String toString() {
		return "Cart [apple=" + apple + ", orange=" + orange + ", melon="
				+ melon + "]";
	}
}

카트를 생성해줍니다.


ShoppingController.java

@RestController
public class ShoppingController {

	@RequestMapping("/")
	private String test() {
		ThreadRepository.remove();
		
		this.addMelon();
		
		return ThreadRepository.getCart().toString();
	}
	
	private void addMelon() {
		ThreadRepository.getCart().addMelon();
	}
}

컨트롤러가 위처럼 수정되었습니다.  ThreadRepository에서 getCart를 했을때 해당 요청 쓰래드에 대한 Cart가 나오기 때문에 소스 어떤부분에서든 해당 카트의 데이터를 넣고 빼는 것이 가능합니다.

이때 ThreadLocal을 이용하였기 때문에 요청 시 새로운 스래드가 생성되며 그 스래드에서만 사용할 수 있게 관리됩니다.


* 일반으로 WAS는 쓰래드풀을 쓰기 때문에 요청이나 응답 시 해당 Threadlocal을 초기화 하지 않을 경우 이전에 사용한 정보가 남아 있습니다.  쓰래드 재사용시 반드시 remove를 통해 초기화 시켜주어야 합니다.



참고소스

https://github.com/donnert/spring-boot/tree/master/thread-local




  Comments,   0  Trackbacks
댓글 쓰기