비동기 논블록킹 &비동기 처리 후 결과값 대기(waitForAsync)
Tags: async, nonblock, waitforasync
Published: 2023-10-12 00:15:56

- 비동기-논블록킹 (어노테이션 기반)
    - 신규 스레드로 callback 처리까지 ( 진정한 비동기-논블록킹)

```java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Async {
}

public class AsyncBeanProcessor {

    private ExecutorService executorService;
    private ExecutorService responseExecutorService;

    public AsyncBeanProcessor(int threadPoolSize) {
        executorService = Executors.newFixedThreadPool(threadPoolSize); // 비동기 작업용 스레드 풀 생성
        responseExecutorService = Executors.newCachedThreadPool(); // 응답 처리용 스레드 풀 생성
    }

    public void processAsyncMethods(Object bean) {
        Class<?> clazz = bean.getClass();
        for (java.lang.reflect.Method method : clazz.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Async.class)) {
                executorService.submit(() -> {
                    try {
                        Object result = method.invoke(bean); // 비동기로 어노테이션이 붙은 메소드 실행
                        if (result instanceof ResponseBean) {
                            ResponseBean responseBean = (ResponseBean) result;
                            responseBean.waitForAsync(); // 응답이 오기를 대기
                            responseExecutorService.submit(() -> processResponse(responseBean)); // 응답 처리
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
            }
        }
    }

    private void processResponse(ResponseBean responseBean) {
        // 응답 처리 로직 구현
        System.out.println("비동기 작업 응답: " + responseBean.getResponse());
    }

    public void shutdown() {
        executorService.shutdown(); // 비동기 작업용 스레드 풀 종료
        responseExecutorService.shutdown(); // 응답 처리용 스레드 풀 종료
    }
}

public class ResponseBean {

    private Object response;
    private CountDownLatch latch;

    public ResponseBean() {
        latch = new CountDownLatch(1);
    }

    public void setResponse(Object response) {
        this.response = response;
        latch.countDown(); // 응답이 설정되었음을 알림
    }

    public Object getResponse() {
        return response;
    }

    public void waitForAsync() throws InterruptedException {
        latch.await(); // 응답이 올 때까지 대기
    }
}
```

위의 코드에서 AsyncBeanProcessor 클래스에서는 비동기 작업이 완료된 후 ResponseBean 객체를 반환하면, 해당 객체의 waitForAsync 메소드를 호출하여 결과 응답이 올 때까지 대기합니다. 대기 중인 스레드는 CountDownLatch를 사용하여 동기화합니다.

ResponseBean 클래스는 비동기 작업의 결과 응답을 저장하는 역할을 합니다. setResponse 메소드를 통해 응답을 설정하고, waitForAsync 메소드를 호출하여 응답이 올 때까지 대기할 수 있습니다.

변경된 코드를 사용하여 비동기 작업 및 응답 처리를 수행할 수 있습니다. 예를 들어:

public class MyService {

```java
@Async
public ResponseBean performAsyncTask() {
    ResponseBean responseBean = new ResponseBean();

    // 비동기로 실행될 작업 내용
    System.out.println("비동기 작업 실행");
    // ...

    // 작업 완료 후 응답 설정
    responseBean.setResponse("작업 완료");

    return responseBean;
}

public void someOtherMethod() {
    // ...
}
```

}

위의 MyService 클래스에서 performAsyncTask 메소드는 비동기 작업을 수행하고 작업이 완료되면 ResponseBean 객체에 응답을 설정합니다.

다음은 변경된 MyApp 클래스의 예시입니다:

public class MyApp {

```java
public static void main(String[] args) {
    MyService myService = new MyService();
    AsyncBeanProcessor beanProcessor = new AsyncBeanProcessor(10); // 스레드 풀 크기 지정

    beanProcessor.processAsyncMethods(myService);

    // 어플리케이션 종료 시 Executor 종료
    Runtime.getRuntime().addShutdownHook(new Thread(beanProcessor::shutdown));
}
```

}

이렇게 변경된 코드로 비동기 작업을 요청하고, waitForAsync 메소드를 호출하여 결과 응답이 올 때까지 스레드가 대기하는 방식으로 처리할 수 있습니다.

---

- 비동기-논블록킹 (어노테이션 기반)
    - 응답 waitForAsync 호출시 , 주 스레드 동기 방식으로 대기

```java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Async {
}

public class AsyncBeanProcessor {

    private ExecutorService executorService;

    public AsyncBeanProcessor(int threadPoolSize) {
        executorService = Executors.newFixedThreadPool(threadPoolSize); // 비동기 작업용 스레드 풀 생성
    }

    public void processAsyncMethods(Object bean) {
        Class<?> clazz = bean.getClass();
        for (java.lang.reflect.Method method : clazz.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Async.class)) {
                executorService.submit(() -> {
                    try {
                        Object result = method.invoke(bean); // 비동기로 어노테이션이 붙은 메소드 실행
                        if (result instanceof ResponseBean) {
                            ResponseBean responseBean = (ResponseBean) result;
                            responseBean.waitForAsync(); // 응답이 오기를 대기
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
            }
        }
    }

    public void shutdown() {
        executorService.shutdown(); // 비동기 작업용 스레드 풀 종료
    }
}

public class ResponseBean {

    private Object response;
    private final Object lock = new Object();

    public void setResponse(Object response) {
        this.response = response;
        synchronized (lock) {
            lock.notify(); // 대기 중인 스레드를 깨움
        }
    }

    public Object getResponse() {
        return response;
    }

    public void waitForAsync() throws InterruptedException {
        synchronized (lock) {
            lock.wait(); // 응답이 올 때까지 호출한 스레드를 대기
        }
    }
}
```

위의 코드에서 ResponseBean 클래스는 waitForAsync 메소드가 호출된 스레드를 대기시키고, setResponse 메소드가 호출되면 대기 중인 스레드를 깨웁니다. 이를 위해 lock 객체와 synchronized 블록을 사용하여 동기화합니다.

이제 수정된 코드로 비동기 작업을 요청하고, waitForAsync 메소드를 호출하여 결과 응답이 올 때까지 호출한 스레드가 대기하는 방식으로 처리할 수 있습니다.