Reactive-Programming

함수 관점에서 동기와 비동기 Blocking, Non-blocking

caporatang 2024. 4. 14. 02:41
반응형

함수 호출 관점에서 동기와 비동기

동기 비동기를 알기 전에 알아두면 좋은 개념

  • Caller, Callee
    • 함수가 다른 함수를 호출하는 상황에서
      Caller : 호출하는 함수
      Callee : 호출 당하는 함수
  • 함수형 인터페이스
    • 함수형 프로그래밍을 지원하기 위해 java 8부터 도입
    • 1개의 추상 메서드를 갖고 있는 인터페이스
    • 함수를 1급 객체로 사용할 수 있다.
    • Function, Consumer, Supplier, Runnable.. 등
    • 함수형 인터페이스를 구현한 익명 클래스를 람다식으로 변경 가능하다.

functionalInterface

  • 1급객체, 람다, 함수형 인터페이스를 인자로 넘기는 예제

    @Slf4j
    public class FunctionalInterface {
    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);
    }
    }

동기와 비동기의 간단한 코드에서의 비교

A class

    @Slf4j
    public class A {

        public static void main(String[] args) {
            log.info("Start main");

            // caller가 callee(getResult) 호출
            var result = getResult();

            // callee가 반환해준 value를 가지고 다음 excute(액션)을 수행
            var nextValue = result + 1;
            assert nextValue == 1;

            log.info("Finish main");
        }

        private static int getResult() {
            log.info("Start getResult");

            try {
            // 1초 슬립! 메인 함수는 최소한 1초의 시간이 걸린다.
                Thread.sleep(1000); 
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            var result = 0;

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

B class

    @Slf4j
    public class B {    
        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> cb) {
            log.info("Start getResult");
            try {
               Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            var result = 0;
            cb.accept(result);
            log.info("Finish getResult");
        }
    }

A , B 클래스의 차이

  • A class : main은 getResult의 결과에 관심이 있다.
    main은 결과를 이용해서 다음 코드를 실행한다.
    우리가 보통 자주 사용하는 동기식 프로그래밍 방법이다.
  • B class : main은 getResult의 결과에 관심이 없다.
    getResult는 결과를 이용해서 함수형 인터페이스를 실행한다
    비동기 프로그래밍이다.

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

A,B 모델의 공통점

  • caller (main) 가 getResult를 호출하고 A모델은 getReuslt가 반환될때까지, B모델은 getResult에서 callback 까지 실행한 후까지 아무런 동작도 수행하지 못한다.

  • 이를 Blocking 이라고 한다.

  • Blocking
    caller가 callee를 호출한 후, callee가 완료되기 전까지 caller가 아무것도 할 수 없다.
    제어권을 callee가 가지고 있다.


Non-blocking [C class]

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

          var count = 1;
            Future<Integer> result = getResult();
            // getResult에 대한 작업이 끝났는지 계속 확인한다.
            while(!result.isDone()) {
                log.info("wating 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 excutor = Executors.newSingleThreadExecutor();
            try {
                return excutor.submit(new Callable<Integer>() {
                    @Override
                    public Integer call() throws Exception {
                        log.info("Start getResult");
                        try {
                            // 1초동안 sleep -> 1초동안 isDone은 false
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                        var result = 0;
                        try {
                            return result;
                        } finally {
                            log.info("Finish getResult");
                        }
                    }
                });
            } finally {
                excutor.shutdown();
            }
        }
  • callee를 호출한 후, callee가 완료되지 않더라도 caller는 본인의 일을 할 수 있다.
    제어권을 caller가 가지고 있다.

Blocking Non-blocking
설명 Callee가 완료될 때까지 대기 Caller는 기다리지 않고 진행
제어 Callee가 제어권을 가짐 Caller가 제어권을 가짐
스레딩 별도의 스레드가 필요X 별도의 스레드가 필요

비동기 Non-blocking [D class]

    @Slf4j
    public class D {
        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");
        }

        private static void getResult(Consumer<Integer> callbackc) {
            // 별도의 스레드로 동작된다.
            var excutor = Executors.newSingleThreadExecutor();
            try {
                excutor.submit(new Runnable() {
                    @Override
                    public void run() {
                        log.info("Start getResult");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        var result = 0;
                        try {
                            callbackc.accept(result);
                        } finally {
                            log.info("Finish getResult");
                        }

                    }
                });
            } finally {
                excutor.shutdown();
            }
        }
    }

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

정리하자면..

반응형

'Reactive-Programming' 카테고리의 다른 글

Project reactor  (0) 2024.04.21
HotPublisher, ColdPublisher  (0) 2024.04.17
Reactive_streams  (0) 2024.04.17
Reactive programming  (0) 2024.04.16
Reactive manifesto  (0) 2024.04.15