본문 바로가기
코딩일기/날씨앱 만들기 프로젝트

[Java] CompletionHandler를 이용한 Thread 콜백 구현

by 욱파이어니어 2021. 12. 2.
728x90
반응형

CompletionHandler란?

 

출처 : https://homoefficio.github.io/

 

우리가 Multi Thread를 사용하는 주 목적은 어떤 작업을 병렬적으로 일을 처리하기 위해서이다.

 

내가 1670건의 API를 호출하려것도 당연히 Single Thread가 아닌 Multi Thread로 해야지 작업 처리 시간을

줄일수가 있다. 그러기 위해서 내가 Thread Pool을 공부하기도 했고.

 

https://wpioneer.tistory.com/228

 

[Java] Thread Pool 설명 및 사용 방법

이번엔 Thread를 좀 더 유연하게 활용할수 있는 Thread Pool에 대해서 알아볼 예정이다. Thread Pool 이란? Thread Pool은 미리 대기시켜놓은 Thread를 가지고 큐 안에 있는 작업들을 실행하는것이다. 이렇게

wpioneer.tistory.com

 

하지만 내가 단지 Thread를 실행시키고 끝내는 거라면 굳이 CompletionHandler를 사용할 필요가 없다.

왜냐하면 해당 작업으로 받아와야 할 값이 없으니까.

 

하지만 나같은 경우는 Multi Thread를 통해서 API를 호출하고 result값을 받아 와야 하기때문에 

CompletionHandler를 사용해야 했다.

 

이처럼 CompletionHandler는 위 사진처럼 비동기 방식으로 return값을 받아야 할때 사용하는 Class 객체이다.

 

그럼 다른 Thread들은 현재 Thread의 응답 결과를 받을때까지 대기할 필요 없이

자기의 작업을 마치고 응답(CallBack) 을 기다리면 된다. 

 

 

위 사진에서 CompletionHandler가 하는 역할은 진동벨 역할을 해주는것이다.

 

그럼 이제 CompletionHanlder 사용법에 대해서 알아보자.

 

CompletionHandler 사용법

 

1. CompletionHandler 인스턴스를 생성한다.

2. Thread에서 넘겨받은 인자를 사용하여 CallBack 메소드를 호출한다.

 

 

이정도의 순서로 진행이 되는데 각각의 순서에 대해 자세히 알아보자.

 

1. CompletionHandler 인스턴스를 생성한다.

 

아래의 코드처럼 메인 스레드에서 CompletionHandler를 생성한다.

		System.out.println("Main Thread Start");
		System.out.println(Calendar.getInstance().getTime());
		
		//ExecutorService 인터페이스 구현객체 Executors 정적 메소드를 통해 최대 스레드 개수가 3인 스레드 풀을 만듬
		CountDownLatch countDownLatch = new CountDownLatch(10);
		ExecutorService exs = Executors.newFixedThreadPool(5);
		
		List<TestDto> tdl = new ArrayList<TestDto>();
		
		
		//CompletionHandler 인스턴스를 생성한다.
		CompletionHandler<TestDto,Void> callBack = 
				new CompletionHandler<TestDto,Void>(){

					//성공했을떄
					@Override
					public void completed(TestDto result, Void attachment) {
						// TODO Auto-generated method stub
						if(result == null) {
							System.out.println("found it");
							System.exit(0);
						}
						System.out.println("CallBack : "+result.toString());
						tdl.add(result); //전달받은 객체 인자를 list에 추가함
					}
					
					//실패했을때
					@Override
					public void failed(Throwable exc, Void attachment) {
						// TODO Auto-generated method stub
						System.out.println("failed");
					}
			
		};
		
		for(int i = 0;i<10;i++) {
			exs.submit(new ThreadCallback("Thread"+i,exs,countDownLatch,callBack));
		}
		
		exs.shutdown(); //thread pool로 다 호출하고 나면은 thread pool을 끝냄
		countDownLatch.await(); //CountDownLatch가 다 끝날때까지 기다린다.
		System.out.println("Main Thread End");
		System.out.println(tdl.size());
		
		for(TestDto td : tdl) {
			System.out.println(td.toString()); //질문 2. list에 추가되어있는 TestDto 객체가 왜 null 값인가요?
		}
		
		System.out.println(Calendar.getInstance().getTime());

 

CompletionHandler를 생성할때 <> 안에 들어가는것은 공식문서에 따르면 아래와 같다.

 

 

V는 I/O 작업에서의 return 되는 값의 타입을 얘기하는것이고

A는 I/O 작업을 실행할때 사용되는? 객체의 타입을 얘기한다.

 

나같은 경우는 return 되는 값의 타입은 TestDto이고 A는 작업을 실행할때 사용되는 객체의 타입이 없어 Void 형태로

지정을 해뒀다.

 

 

2. Thread에서 넘겨받은 인자를 사용하여 CallBack 메소드를 호출한다.

 

나같은 경우는 ThreadPool에서 Thread 객체를 생성할때 생성자 인자로 CompletionHandler를 넘겨주었다.

 

	public ThreadCallback(String threadName, ExecutorService exs, CountDownLatch cdl,
			CompletionHandler<TestDto, Void> callBack) {
		super();
		this.threadName = threadName;
		this.te = (ThreadPoolExecutor) exs;
		this.cdl = cdl;
		this.callBack = callBack;
	}

 

그리고 나서 run()에서 작업이 완료되었을때 넘겨받은 CompletionHandler인자로 

completed()를 호출하였다. 

 

	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println(te.getPoolSize());
		System.out.println("API call");
		try {
			Thread.sleep(6000);
			System.out.println("API call success");
			TestDto td = new TestDto(threadName,te.getPoolSize()); //여기서 객체를 만드는데 객체가 null임
			callBack.completed(td, null); //callback 메소드를 호출했을때 클래스 객체를 넘겨줌
			System.out.println();
			cdl.countDown();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

 

 

이렇게 해서 비동기적으로 작업을 실행할떄 CallBack메소드를 사용하여서 결과값을 받을수 있게 되었다.

반응형