동기, 비동기, Blocking, Non-Blocking를 Caller와 Calle로 이해하기

반응형
728x90
반응형

Caller와 Callee

함수가 다른 함수를 호출하는 상황

  • Caller : 호출하는 함수
  • Callee : 호출 당하는 함수

 

 

함수형 인터페이스

함수형 인터페이스는 호출한 쓰레드에서 실행된다.

@Slf4j
public class p030_FunctionalInterfaceExample {
    public static void main(String[] args) {
        var consumer = getConsumer();
        consumer.accept(1);

        var consumerAsLambda = getConsumerAsLambda();
        consumerAsLambda.accept(1);

        handleConsumer(consumer);
    }

    public static Consumer<Integer> getConsumer() {
        Consumer<Integer> returnValue = new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                log.info("value in interface: {}", integer);
            }
        };
        return returnValue;
    }

    public static Consumer<Integer> getConsumerAsLambda() {
        return integer -> log.info("value in lambda: {}", integer);
    }

    public static void handleConsumer(Consumer<Integer> consumer) {
        log.info("handleConsumer");
        consumer.accept(1);
    }
}

 

1) 함수형 인터페이스의 메서드 호출

위 호출을 main 메서드에서 했으므로, main 메서드에서 함수형 인터페이스의 메서드가 수행된다.

consumer.accept(1)

 

 

 

실행결과
05:33:36.132 [main] INFO com.example03.asyncprogramming.p030_FunctionalInterfaceExample - value in interface: 1
05:33:36.134 [main] INFO com.example03.asyncprogramming.p030_FunctionalInterfaceExample - value in lambda: 1
05:33:36.134 [main] INFO com.example03.asyncprogramming.p030_FunctionalInterfaceExample - handleConsumer
05:33:36.134 [main] INFO com.example03.asyncprogramming.p030_FunctionalInterfaceExample - value in interface: 1

 

 

동기 & 블로킹

@Slf4j
public class p031_SyncBlockingExample {
    public static void main(String[] args) {
        log.info("Start main");
        var result = getResult();
        var nextValue = result + 1;
        assert nextValue == 1;
        log.info("Finish main");
    }

    public static int getResult() {
        log.info("Start getResult");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        var result = 0;
        try {
            return result;
        } finally {
            log.info("Finish getResult");
        }
    }
}

 

실행결과

실행 결과를 보면, 1초 대기 후 이후 로직이 실행됨을 알 수 있다.

05:37:06.903 [main] INFO com.example03.asyncprogramming.p031_SyncBlockingExample - Start main
05:37:06.905 [main] INFO com.example03.asyncprogramming.p031_SyncBlockingExample - Start getResult
05:37:07.920 [main] INFO com.example03.asyncprogramming.p031_SyncBlockingExample - Finish getResult
05:37:07.921 [main] INFO com.example03.asyncprogramming.p031_SyncBlockingExample - Finish main

 

 

  • main는 getResult의 결과에 관심이 있다. (동기)
  • main은 결과를 이용해서 다음 코드를 실행한다.
  • main은 getResult가 결과를 돌려주기 전까지 아무것도 할 수 없다. (블로킹)
  • main은 getResult가 완료될때까지 대기한다. (블로킹)
  • getResult를 호출한 후, getResult가 완료되지 않으면 main은 본인의 일을 할 수가 없다.

 

 

비동기 & 블로킹

@Slf4j
public class p032_AsyncBlockingExample {
    public static void main(String[] args) {
        log.info("Start main");
        // main은 getResult의 결과에 관심이 없다.
        // getResult 결과를 이용해서 함수형 인터페이스를 실행한다.
        getResult(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                var nextValue = integer + 1;
                assert nextValue == 1;
            }
        });
        log.info("Finish main");
    }

    // ac
    public static void getResult(
            Consumer<Integer> callback
    ) {
        log.info("Start getResult");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        var result = 0;
        try {
            callback.accept(result);
        } finally {
            log.info("Finish getResult");
        }
    }
}

 

실행결과
05:38:13.975 [main] INFO com.example03.asyncprogramming.p032_AsyncBlockingExample - Start main
05:38:13.977 [main] INFO com.example03.asyncprogramming.p032_AsyncBlockingExample - Start getResult
05:38:15.000 [main] INFO com.example03.asyncprogramming.p032_AsyncBlockingExample - Finish getResult
05:38:15.000 [main] INFO com.example03.asyncprogramming.p032_AsyncBlockingExample - Finish main

 

 

  • main은 getResult의 결과에 관심이 없다. (비동기)
  • getResult는 결과를 이용해서 함수형 인터페이스를 실행한다.
  • main은 getResult가 결과를 구하고 callback을 실행하기 전까지 아무것도 할 수 없다. (블로킹)
  • main은 getResult가 완료될때까지 대기한다. (블로킹)

 

 

동기 vs 비동기

동기 비동기
caller는 callee의 결과에 관심이 있다. caller는 callee의 결과에 관심이 없다.
caller는 결과를 이용해서 action을 수행한다. callee는 결과를 이용해서 callback을 수행한다.

 

 

Blocking

  • callee를 호출한 후, callee가 완료되기 전까지 caller가 아무것도 할 수 없다.
  • 제어권을 callee가 가지고있다.
  • caller와 다른 별도의 thread가 필요하지 않다. 혹은 thread를 추가로 쓸 수도 있다.

 

 

Non-Blocking

  • callee를 호출한 후, callee가 완료되지 않더라도 caller는 본인의 일을 할 수 있다.
  • 제어권을 caller가 가지고있다.
  • caller와 다른 별도의 thread가 필요하다.

 

 

동기 & 논블로킹

@Slf4j
public class p043_SyncNonBlockingExampleRunner {
    public static void main(String[] args)
            throws InterruptedException, ExecutionException {
        log.info("Start main");

        var count = 1;

        Future<Integer> result = getResult();
        while (!result.isDone()) {
            log.info("Waiting for result, count: {}", count++);
            Thread.sleep(100);
        }

        var nextValue = result.get() + 1;
        assert nextValue == 1;

        log.info("Finish main");
    }

    public static Future<Integer> getResult() {
        var executor = Executors.newSingleThreadExecutor();
        try {
            return executor.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    log.info("Start getResult");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    var result = 0;
                    try {
                        return result;
                    } finally {
                        log.info("Finish getResult");
                    }
                }
            });
        } finally {
            executor.shutdown();
        }
    }
}

 

실행결과
05:53:33.806 [main] INFO com.example03.asyncprogramming.p043_SyncNonBlockingExampleRunner - Start main
05:53:33.810 [pool-1-thread-1] INFO com.example03.asyncprogramming.p043_SyncNonBlockingExampleRunner - Start getResult
05:53:33.810 [main] INFO com.example03.asyncprogramming.p043_SyncNonBlockingExampleRunner - Waiting for result, count: 1
05:53:33.928 [main] INFO com.example03.asyncprogramming.p043_SyncNonBlockingExampleRunner - Waiting for result, count: 2
05:53:34.104 [main] INFO com.example03.asyncprogramming.p043_SyncNonBlockingExampleRunner - Waiting for result, count: 3
05:53:34.277 [main] INFO com.example03.asyncprogramming.p043_SyncNonBlockingExampleRunner - Waiting for result, count: 4
05:53:34.452 [main] INFO com.example03.asyncprogramming.p043_SyncNonBlockingExampleRunner - Waiting for result, count: 5
05:53:34.627 [main] INFO com.example03.asyncprogramming.p043_SyncNonBlockingExampleRunner - Waiting for result, count: 6
05:53:34.800 [main] INFO com.example03.asyncprogramming.p043_SyncNonBlockingExampleRunner - Waiting for result, count: 7
05:53:34.886 [pool-1-thread-1] INFO com.example03.asyncprogramming.p043_SyncNonBlockingExampleRunner - Finish getResult
05:53:34.967 [main] INFO com.example03.asyncprogramming.p043_SyncNonBlockingExampleRunner - Finish main

 

  • main는 getResult의 결과에 관심이 있다. (동기)
  • getResult를 호출한 후, getResult가 완료되지 않더라도 main은 본인의 일을 할 수 있다. (논블로킹)

 

 

비동기 & 논블로킹

@Slf4j
public class p048_AsyncNonBlockingExample {
    public static void main(String[] args) {
        log.info("Start main");
        getResult(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                var nextValue = integer + 1;
                assert nextValue == 1;
            }
        });
        log.info("Finish main");
    }

    public static void getResult(Consumer<Integer> callback) {
        var executor = Executors.newSingleThreadExecutor();
        try {
            executor.submit(new Runnable() {
                @Override
                public void run() {
                    log.info("Start getResult");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    var result = 0;
                    try {
                        callback.accept(result);
                    } finally {
                        log.info("Finish getResult");
                    }
                }
            });
        } finally {
            executor.shutdown();
        }
    }
}

 

실행결과
05:57:58.623 [main] INFO com.example03.asyncprogramming.p048_AsyncNonBlockingExample - Start main
05:57:58.627 [main] INFO com.example03.asyncprogramming.p048_AsyncNonBlockingExample - Finish main
05:57:58.627 [pool-1-thread-1] INFO com.example03.asyncprogramming.p048_AsyncNonBlockingExample - Start getResult
05:57:59.680 [pool-1-thread-1] INFO com.example03.asyncprogramming.p048_AsyncNonBlockingExample - Finish getResult

 

  • main은 getResult의 결과에 관심이 없다. (비동기)
  • getResult를 호출한 후, getResult가 완료되지 않더라도 main은 본인의 일을 할 수 있다. (논블로킹)

 

 

동기 & 블로킹

비동기 & 블로킹

동기 & 논블로킹

비동기 & 논블로킹

 

 

함수 호출 모델

  동기 비동기
Blocking caller는 아무것도 할 수 없는 상태가 된다.
결과를 얻은 후 직접 처리한다.
caller는 아무것도 할 수 없는 상태가 된다.
결과는 callee가 처리한다.
Non-Blocking caller는 자기 할일을 할 수 있다.
결과를 얻은 후 직접 처리한다.
caller는 자기 할일을 할 수 있다.
결과는 callee가 처리한다.

 

 

'Spring Webflux 완전 정복 : 코루틴부터 리액티브 MSA 프로젝트까지' 강의 참고

 

 

반응형

Designed by JB FACTORY