728x90

스프링을 공부하기 전에 항상 나오는 말이 있다.

스프링은 동기, node 서버는 비동기라는 말을 굉장히 많이 들었던 것 같다.

처음에 공부할 때는 외우고만 있다가 해당 내용을 운영체제에서 공부하고 그때서야 이해할 수 있게 되었다.

 

이번에도 공부하기 전에 동기와 비동기에 대해서 정리하고 가고자 한다.

 

Caller와 Callee

프로그래밍에서 굉장히 많이 봤던 용어일 것이다.

caller는 말 그대로 다른 함수를 호출하는 함수이고, callee는 그때에 호출당하는 함수이다.

 

함수형 인터페이스

1개의 추상 메서드를 가지고 있는 인터페이스 함수를 1급 객체로 사용하는 것을 말한다.

자바에서는 람다 표현식을 이 함수형 인터페이스에서만 사용 가능하다.

package org.example;

@FunctionalInterface
public interface Function<T> {

    void accept(T t);
}

 

해당 함수형 인터페이스를 사용해보도록 하자.

package org.example;

public class Main {

    public static void main(String[] args) {
        var function = getFunction();
        function.accept(1);

        var functionAsLambda = getFunctionAsLambda();
        functionAsLambda.accept(1);

        functionHandler(function);
    }

    public static Function<Integer> getFunction(){
        Function<Integer> returnValue = new Function<Integer>() {
            @Override
            public void accept(Integer integer) {
                myLog("value in interface: " + integer);
            }
        };
        return returnValue;
    }

    public static Function<Integer> getFunctionAsLambda(){
        return integer -> myLog("value in lambda: " + integer);
    }

    public static void functionHandler(Function<Integer> function){
        myLog("functionHandler");
        function.accept(1);
    }


    public static void myLog(String string){
        System.out.println(Thread.currentThread() + " -> " +string);
    }
}

 

이 코드를 실행해보면 

 

이때 모두 메인 스레드에서 실행이 되는 것을 볼 수 있다.

 

함수 호출 관점

 

A 프로그램

package org.example;

public class programA {

    public static void myLog(String string){
        System.out.println(Thread.currentThread() + " -> " +string);
    }

    public static void main(String[] args) {
        myLog("start main");
        var value = getValue();
        var nextValue = value + 1;
        myLog(String.valueOf(value == nextValue));
        myLog("Finish main");
    }

    public static int getValue(){
        myLog("start getValue");
        try{
            Thread.sleep(1000);
        }catch (InterruptedException e){
            throw new RuntimeException(e);
        }

        var value = 0;
        try{
            return value;
        }finally {
            myLog("Finish getResult");
        }
    }
}

 

이 모델의 실행을 그림으로 표현하면 다음과 같다.

main 함수는 getValue의 실행이 끝날 때까지 기다린 후 해당 데이터를 가지고 작업을 한다.

 

이것과는 다른 B 프로그램이다.

package org.example;

public class programB {

    public static void myLog(String string){
        System.out.println(Thread.currentThread() + " -> " +string);
    }

    public static void main(String[] args) {
        myLog("start main");
        getValue(integer -> {
            var nextValue = integer + 1;
            myLog(String.valueOf(nextValue == 1));
        });

        myLog("Finish main");
    }

    public static void getValue(Function<Integer> function){
        myLog("Start getValue");
        try{
            Thread.sleep(1000);
        }catch (InterruptedException e){
            throw new RuntimeException(e);
        }

        var value = 0;
        function.accept(value);
        myLog("Finish getValue");
    }
}

 

해당 프로그램은 main에서 직접 실행하는 것이 아니라 getValue에서 실행하도록 한다.

 

이 프로그램을 그림으로 나타내면 다음과 같다.

 

두 프로그램의 차이점은
A 프로그램은 다음 코드를 위해 callee의 반환값이 필요하다.

B 프로그램은 다음 코드를 위해 callee의 반환값이 필요하지 않고 callee가 결과를 이용해서 callback을 수행한다.

 

이러면 A 프로그램을 동기, B 프로그램을 비동기라고 생각을 하면 된다.

 

+ Recent posts